Download do Dataset "The Movies Dataset" do Kaggle¶
Para baixar e utilizar o dataset diretamente no seu notebook, siga os passos abaixo:
1. Instalar a Biblioteca Kaggle¶
Primeiro, certifique-se de que a biblioteca kaggle está instalada. Se não estiver, você pode instalá-la com o seguinte comando:
!pip install kaggle
# !pip install kaggle
# !pip install mlxtend
2. Download do Dataset¶
Nesta seção, estamos realizando o download do "The Movies Dataset" do Kaggle diretamente para o nosso ambiente de desenvolvimento.
Baixando o Dataset¶
O comando a seguir utiliza a biblioteca kaggle para fazer o download do dataset diretamente do site do Kaggle:
!kaggle datasets download -d rounakbanik/the-movies-dataset
Após o download, o dataset está compactado em um arquivo ZIP. Para utilizá-lo, precisamos extrair os arquivos contidos nele. O código abaixo realiza essa extração:
import zipfile
import os
with zipfile.ZipFile("the-movies-dataset.zip", "r") as zip_ref:
zip_ref.extractall("the-movies-dataset")
Análise Exploratória dos Dados do Conjunto "The Movies Dataset"¶
Neste notebook, realizaremos uma análise exploratória completa do conjunto de dados "The Movies Dataset" disponível no Kaggle. Esta análise incluirá o carregamento dos dados, compreensão da sua estrutura, limpeza dos dados e realização de várias análises para obter insights sobre a indústria cinematográfica.
#Fazendo as instalações necessárias
# !pip install wordcloud
# !pip install textblob
# nltk.download('punkt')
# nltk.download('stopwords')
# Importando as bibliotecas necessárias
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
from wordcloud import WordCloud, STOPWORDS
from mlxtend.frequent_patterns import apriori, association_rules
from tqdm import tqdm
import random
from collections import Counter
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import re
from wordcloud import WordCloud
from nltk.util import ngrams
import ast
from textblob import TextBlob
pio.renderers.default = "notebook"
# Configurando o estilo dos gráficos
sns.set(style="whitegrid")
Carregando os Dados¶
Vamos começar carregando os arquivos CSV disponíveis no conjunto de dados. Esses arquivos contêm diversas informações relacionadas a filmes, como créditos, links, metadados dos filmes, avaliações, entre outros.
# Carregando os diferentes arquivos CSV
links_small = pd.read_csv('the-movies-dataset/links_small.csv')
movies_metadata = pd.read_csv('the-movies-dataset/movies_metadata.csv')
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3125572303.py:3: DtypeWarning: Columns (10) have mixed types. Specify dtype option on import or set low_memory=False.
Explorando a Estrutura dos Dados¶
Nesta seção, vamos explorar a estrutura de cada um dos arquivos carregados, analisando o número de linhas e colunas, o tipo de dados presente em cada coluna, e identificando valores nulos ou ausentes. Utilizaremos apenas os datasets links_small.csv e movies_metadata.csv, pois são versões menores das informações sobre os filmes.
# Exibindo a estrutura dos dados dos datasets
print("\nEstrutura de Movies Metadata:")
print(movies_metadata.head())
print(movies_metadata.info())
print("\nEstrutura de Links Small:")
print(links_small.head())
print(links_small.info())
Estrutura de Movies Metadata:
adult belongs_to_collection budget \
0 False {'id': 10194, 'name': 'Toy Story Collection', ... 30000000
1 False NaN 65000000
2 False {'id': 119050, 'name': 'Grumpy Old Men Collect... 0
3 False NaN 16000000
4 False {'id': 96871, 'name': 'Father of the Bride Col... 0
genres \
0 [{'id': 16, 'name': 'Animation'}, {'id': 35, '...
1 [{'id': 12, 'name': 'Adventure'}, {'id': 14, '...
2 [{'id': 10749, 'name': 'Romance'}, {'id': 35, ...
3 [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam...
4 [{'id': 35, 'name': 'Comedy'}]
homepage id imdb_id original_language \
0 http://toystory.disney.com/toy-story 862 tt0114709 en
1 NaN 8844 tt0113497 en
2 NaN 15602 tt0113228 en
3 NaN 31357 tt0114885 en
4 NaN 11862 tt0113041 en
original_title \
0 Toy Story
1 Jumanji
2 Grumpier Old Men
3 Waiting to Exhale
4 Father of the Bride Part II
overview ... release_date \
0 Led by Woody, Andy's toys live happily in his ... ... 1995-10-30
1 When siblings Judy and Peter discover an encha... ... 1995-12-15
2 A family wedding reignites the ancient feud be... ... 1995-12-22
3 Cheated on, mistreated and stepped on, the wom... ... 1995-12-22
4 Just when George Banks has recovered from his ... ... 1995-02-10
revenue runtime spoken_languages \
0 373554033.0 81.0 [{'iso_639_1': 'en', 'name': 'English'}]
1 262797249.0 104.0 [{'iso_639_1': 'en', 'name': 'English'}, {'iso...
2 0.0 101.0 [{'iso_639_1': 'en', 'name': 'English'}]
3 81452156.0 127.0 [{'iso_639_1': 'en', 'name': 'English'}]
4 76578911.0 106.0 [{'iso_639_1': 'en', 'name': 'English'}]
status tagline \
0 Released NaN
1 Released Roll the dice and unleash the excitement!
2 Released Still Yelling. Still Fighting. Still Ready for...
3 Released Friends are the people who let you be yourself...
4 Released Just When His World Is Back To Normal... He's ...
title video vote_average vote_count
0 Toy Story False 7.7 5415.0
1 Jumanji False 6.9 2413.0
2 Grumpier Old Men False 6.5 92.0
3 Waiting to Exhale False 6.1 34.0
4 Father of the Bride Part II False 5.7 173.0
[5 rows x 24 columns]
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 45466 entries, 0 to 45465
Data columns (total 24 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 adult 45466 non-null object
1 belongs_to_collection 4494 non-null object
2 budget 45466 non-null object
3 genres 45466 non-null object
4 homepage 7782 non-null object
5 id 45466 non-null object
6 imdb_id 45449 non-null object
7 original_language 45455 non-null object
8 original_title 45466 non-null object
9 overview 44512 non-null object
10 popularity 45461 non-null object
11 poster_path 45080 non-null object
12 production_companies 45463 non-null object
13 production_countries 45463 non-null object
14 release_date 45379 non-null object
15 revenue 45460 non-null float64
16 runtime 45203 non-null float64
17 spoken_languages 45460 non-null object
18 status 45379 non-null object
19 tagline 20412 non-null object
20 title 45460 non-null object
21 video 45460 non-null object
22 vote_average 45460 non-null float64
23 vote_count 45460 non-null float64
dtypes: float64(4), object(20)
memory usage: 8.3+ MB
None
Estrutura de Links Small:
movieId imdbId tmdbId
0 1 114709 862.0
1 2 113497 8844.0
2 3 113228 15602.0
3 4 114885 31357.0
4 5 113041 11862.0
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9125 entries, 0 to 9124
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 movieId 9125 non-null int64
1 imdbId 9125 non-null int64
2 tmdbId 9112 non-null float64
dtypes: float64(1), int64(2)
memory usage: 214.0 KB
None
Limpeza e Preparação dos Dados¶
Antes de realizar qualquer análise, é importante garantir que os dados estejam limpos e bem preparados. Nesta seção, vamos tratar valores nulos, corrigir tipos de dados, e remover quaisquer inconsistências.
# Removendo linhas onde a coluna 'title' possui valores nulos, pois o título é uma informação essencial
movies_metadata = movies_metadata.dropna(subset=['title'])
# Criando uma nova coluna 'year', que contém apenas o ano extraído da coluna 'release_date'
movies_metadata['year'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce').apply(
lambda x: str(x).split('-')[0] if pd.notnull(x) else np.nan
)
# Convertendo a coluna 'year' para valores numéricos, substituindo valores inválidos (não numéricos) por NaN
movies_metadata['year'] = pd.to_numeric(movies_metadata['year'], errors='coerce')
movies_metadata = movies_metadata.dropna(subset=['year'])
# Extraindo a decada do filme
movies_metadata['decade'] = (movies_metadata['year'] // 10) * 10
# Removendo possíveis linhas duplicadas, se houver mais de uma linha com o mesmo 'id' (coluna identificadora do filme)
movies_metadata = movies_metadata.drop_duplicates(subset='id', keep='first')
# Convertendo a coluna 'budget' e 'revenue' para valores numéricos, substituindo valores inválidos (não numéricos) por NaN
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')
# No dataset links_small, filtramos para manter apenas as linhas onde a coluna 'tmdbId' não tem valores nulos e convertemos para inteiros
links_small_id = links_small[links_small['tmdbId'].notnull()]['tmdbId'].astype('int')
# Exibindo a estrutura do dataset movies_metadata e links_small após a limpeza
print("\nEstrutura de Movies Metadata após a limpeza:")
print(movies_metadata.info())
print("\nEstrutura de Links Small após a limpeza:")
print(links_small.info())
Estrutura de Movies Metadata após a limpeza: <class 'pandas.core.frame.DataFrame'> Index: 45346 entries, 0 to 45465 Data columns (total 26 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 adult 45346 non-null object 1 belongs_to_collection 4485 non-null object 2 budget 45346 non-null int64 3 genres 45346 non-null object 4 homepage 7761 non-null object 5 id 45346 non-null object 6 imdb_id 45332 non-null object 7 original_language 45335 non-null object 8 original_title 45346 non-null object 9 overview 44405 non-null object 10 popularity 45346 non-null object 11 poster_path 45007 non-null object 12 production_companies 45346 non-null object 13 production_countries 45346 non-null object 14 release_date 45346 non-null object 15 revenue 45346 non-null float64 16 runtime 45100 non-null float64 17 spoken_languages 45346 non-null object 18 status 45266 non-null object 19 tagline 20387 non-null object 20 title 45346 non-null object 21 video 45346 non-null object 22 vote_average 45346 non-null float64 23 vote_count 45346 non-null float64 24 year 45346 non-null float64 25 decade 45346 non-null float64 dtypes: float64(6), int64(1), object(19) memory usage: 9.3+ MB None Estrutura de Links Small após a limpeza: <class 'pandas.core.frame.DataFrame'> RangeIndex: 9125 entries, 0 to 9124 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 movieId 9125 non-null int64 1 imdbId 9125 non-null int64 2 tmdbId 9112 non-null float64 dtypes: float64(1), int64(2) memory usage: 214.0 KB None
movies_metadata.columns
Index(['adult', 'belongs_to_collection', 'budget', 'genres', 'homepage', 'id',
'imdb_id', 'original_language', 'original_title', 'overview',
'popularity', 'poster_path', 'production_companies',
'production_countries', 'release_date', 'revenue', 'runtime',
'spoken_languages', 'status', 'tagline', 'title', 'video',
'vote_average', 'vote_count', 'year', 'decade'],
dtype='object')
movies_metadata.shape
(45346, 26)
#Removendo colunas desnecessárias para análise
movies_metadata = movies_metadata.drop(['imdb_id','original_title', 'adult', 'poster_path'], axis=1)
#Removendo linhas com valores nulos e criando uma nova coluna 'ROI' que representa a relação entre a receita e o orçamento
movies_metadata['revenue'] = movies_metadata['revenue'].replace(0, np.nan)
movies_metadata['budget'] = movies_metadata['budget'].replace(0, np.nan)
movies_metadata['ROI'] = movies_metadata['revenue'] / movies_metadata['budget'] - 1
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']
# Estatísticas descritivas para o conjunto de dados de metadados de filmes
movies_metadata.describe()
| budget | revenue | runtime | vote_average | vote_count | year | decade | ROI | profit | |
|---|---|---|---|---|---|---|---|---|---|
| count | 8.876000e+03 | 7.397000e+03 | 45100.000000 | 45346.000000 | 45346.000000 | 45346.000000 | 45346.000000 | 5.375000e+03 | 5.375000e+03 |
| mean | 2.162354e+07 | 6.886594e+07 | 94.177805 | 5.624196 | 110.135293 | 1991.882834 | 1987.431747 | 5.571109e+03 | 5.928381e+07 |
| std | 3.433044e+07 | 1.465125e+08 | 38.348775 | 1.915339 | 491.899276 | 24.053040 | 24.239088 | 2.169776e+05 | 1.396003e+08 |
| min | 1.000000e+00 | 1.000000e+00 | 0.000000 | 0.000000 | 0.000000 | 1874.000000 | 1870.000000 | -9.999995e-01 | -1.657101e+08 |
| 25% | 2.000000e+06 | 2.401510e+06 | 85.000000 | 5.000000 | 3.000000 | 1978.000000 | 1970.000000 | -2.128177e-01 | -1.456000e+06 |
| 50% | 8.000000e+06 | 1.683891e+07 | 95.000000 | 6.000000 | 10.000000 | 2001.000000 | 2000.000000 | 1.057193e+00 | 1.110167e+07 |
| 75% | 2.500000e+07 | 6.731283e+07 | 107.000000 | 6.800000 | 34.000000 | 2010.000000 | 2010.000000 | 3.242222e+00 | 6.223188e+07 |
| max | 3.800000e+08 | 2.787965e+09 | 1256.000000 | 10.000000 | 14075.000000 | 2020.000000 | 2020.000000 | 1.239638e+07 | 2.550965e+09 |
#Filtrando os filmes que tem relação com link_small
movies_metadata['id'] = movies_metadata['id'].astype('int')
movies_metadata = movies_metadata[movies_metadata['id'].isin(links_small_id)]
movies_metadata = pd.merge(movies_metadata, links_small, left_on='id', right_on='tmdbId', how='inner')
movies_metadata
| belongs_to_collection | budget | genres | homepage | id | original_language | overview | popularity | production_companies | production_countries | ... | video | vote_average | vote_count | year | decade | ROI | profit | movieId | imdbId | tmdbId | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | {'id': 10194, 'name': 'Toy Story Collection', ... | 30000000.0 | [{'id': 16, 'name': 'Animation'}, {'id': 35, '... | http://toystory.disney.com/toy-story | 862 | en | Led by Woody, Andy's toys live happily in his ... | 21.946943 | [{'name': 'Pixar Animation Studios', 'id': 3}] | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 7.7 | 5415.0 | 1995.0 | 1990.0 | 11.451801 | 343554033.0 | 1 | 114709 | 862.0 |
| 1 | NaN | 65000000.0 | [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... | NaN | 8844 | en | When siblings Judy and Peter discover an encha... | 17.015539 | [{'name': 'TriStar Pictures', 'id': 559}, {'na... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.9 | 2413.0 | 1995.0 | 1990.0 | 3.043035 | 197797249.0 | 2 | 113497 | 8844.0 |
| 2 | {'id': 119050, 'name': 'Grumpy Old Men Collect... | NaN | [{'id': 10749, 'name': 'Romance'}, {'id': 35, ... | NaN | 15602 | en | A family wedding reignites the ancient feud be... | 11.7129 | [{'name': 'Warner Bros.', 'id': 6194}, {'name'... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.5 | 92.0 | 1995.0 | 1990.0 | NaN | NaN | 3 | 113228 | 15602.0 |
| 3 | NaN | 16000000.0 | [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam... | NaN | 31357 | en | Cheated on, mistreated and stepped on, the wom... | 3.859495 | [{'name': 'Twentieth Century Fox Film Corporat... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.1 | 34.0 | 1995.0 | 1990.0 | 4.090760 | 65452156.0 | 4 | 114885 | 31357.0 |
| 4 | {'id': 96871, 'name': 'Father of the Bride Col... | NaN | [{'id': 35, 'name': 'Comedy'}] | NaN | 11862 | en | Just when George Banks has recovered from his ... | 8.387519 | [{'name': 'Sandollar Productions', 'id': 5842}... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 5.7 | 173.0 | 1995.0 | 1990.0 | NaN | NaN | 5 | 113041 | 11862.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 9077 | NaN | 8000000.0 | [{'id': 18, 'name': 'Drama'}] | NaN | 159550 | en | A man must cope with the loss of his wife and ... | 0.038998 | [{'name': 'Nasser Entertainment', 'id': 35802}] | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 7.0 | 1.0 | 2001.0 | 2000.0 | NaN | NaN | 161944 | 255313 | 159550.0 |
| 9078 | NaN | 1000000.0 | [{'id': 53, 'name': 'Thriller'}, {'id': 10749,... | NaN | 392572 | hi | Rustom Pavri, an honourable officer of the Ind... | 7.333139 | [{'name': 'KriArj Entertainment', 'id': 91689}] | [{'iso_3166_1': 'IN', 'name': 'India'}] | ... | False | 7.3 | 25.0 | 2016.0 | 2010.0 | NaN | NaN | 162542 | 5165344 | 392572.0 |
| 9079 | NaN | 15050000.0 | [{'id': 12, 'name': 'Adventure'}, {'id': 18, '... | NaN | 402672 | hi | Village lad Sarman is drawn to big, bad Mohenj... | 1.423358 | [{'name': 'UTV Motion Pictures', 'id': 2320}, ... | [{'iso_3166_1': 'IN', 'name': 'India'}] | ... | False | 6.7 | 26.0 | 2016.0 | 2010.0 | 0.075083 | 1130000.0 | 162672 | 3859980 | 402672.0 |
| 9080 | NaN | 15000000.0 | [{'id': 28, 'name': 'Action'}, {'id': 12, 'nam... | NaN | 315011 | ja | From the mind behind Evangelion comes a hit la... | 9.285519 | [{'name': 'Cine Bazar', 'id': 5896}, {'name': ... | [{'iso_3166_1': 'JP', 'name': 'Japan'}] | ... | False | 6.6 | 152.0 | 2016.0 | 2010.0 | 4.133333 | 62000000.0 | 163056 | 4262980 | 315011.0 |
| 9081 | NaN | NaN | [{'id': 99, 'name': 'Documentary'}, {'id': 104... | http://www.thebeatlesliveproject.com/ | 391698 | en | The band stormed Europe in 1963, and, in 1964,... | 7.078301 | [{'name': 'Imagine Entertainment', 'id': 23}, ... | [{'iso_3166_1': 'GB', 'name': 'United Kingdom'... | ... | False | 7.6 | 92.0 | 2016.0 | 2010.0 | NaN | NaN | 163949 | 2531318 | 391698.0 |
9082 rows × 27 columns
Ao final da limpeza, temos um dataset movies_metadata contendo as informações sobre os filmes e que já está:¶
- Limpo: O dataset não possui linhas faltantes, e os tipos de dados em cada coluna estão corretamente formatados.
- Apenas com as colunas principais: O dataset contém as principais colunas que vamos utilizar, incluindo tanto as colunas originais quanto as novas relações criadas durante o processo de limpeza.
- Filtrado: O dataset foi filtrado para incluir apenas os principais filmes, conforme a definição do criador do dataset.
movies_metadata.head()
| belongs_to_collection | budget | genres | homepage | id | original_language | overview | popularity | production_companies | production_countries | ... | video | vote_average | vote_count | year | decade | ROI | profit | movieId | imdbId | tmdbId | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | {'id': 10194, 'name': 'Toy Story Collection', ... | 30000000.0 | [{'id': 16, 'name': 'Animation'}, {'id': 35, '... | http://toystory.disney.com/toy-story | 862 | en | Led by Woody, Andy's toys live happily in his ... | 21.946943 | [{'name': 'Pixar Animation Studios', 'id': 3}] | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 7.7 | 5415.0 | 1995.0 | 1990.0 | 11.451801 | 343554033.0 | 1 | 114709 | 862.0 |
| 1 | NaN | 65000000.0 | [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... | NaN | 8844 | en | When siblings Judy and Peter discover an encha... | 17.015539 | [{'name': 'TriStar Pictures', 'id': 559}, {'na... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.9 | 2413.0 | 1995.0 | 1990.0 | 3.043035 | 197797249.0 | 2 | 113497 | 8844.0 |
| 2 | {'id': 119050, 'name': 'Grumpy Old Men Collect... | NaN | [{'id': 10749, 'name': 'Romance'}, {'id': 35, ... | NaN | 15602 | en | A family wedding reignites the ancient feud be... | 11.7129 | [{'name': 'Warner Bros.', 'id': 6194}, {'name'... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.5 | 92.0 | 1995.0 | 1990.0 | NaN | NaN | 3 | 113228 | 15602.0 |
| 3 | NaN | 16000000.0 | [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam... | NaN | 31357 | en | Cheated on, mistreated and stepped on, the wom... | 3.859495 | [{'name': 'Twentieth Century Fox Film Corporat... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 6.1 | 34.0 | 1995.0 | 1990.0 | 4.090760 | 65452156.0 | 4 | 114885 | 31357.0 |
| 4 | {'id': 96871, 'name': 'Father of the Bride Col... | NaN | [{'id': 35, 'name': 'Comedy'}] | NaN | 11862 | en | Just when George Banks has recovered from his ... | 8.387519 | [{'name': 'Sandollar Productions', 'id': 5842}... | [{'iso_3166_1': 'US', 'name': 'United States o... | ... | False | 5.7 | 173.0 | 1995.0 | 1990.0 | NaN | NaN | 5 | 113041 | 11862.0 |
5 rows × 27 columns
Análise Exploratória¶
Agora que os dados estão preparados, vamos explorar as informações presentes no conjunto de dados. Nesta seção, faremos análises estatísticas descritivas, visualizações gráficas e identificaremos tendências e padrões interessantes.
# Calculando a contagem de filmes por década
decade_count = movies_metadata['decade'].value_counts().sort_index()
# Criando o gráfico de barras
fig = px.bar(decade_count, x=decade_count.index, y=decade_count.values,
labels={'x': 'Década', 'y': 'Número de Filmes'},
title='Distribuição de Filmes por Década')
# Ajustando uma linha de tendência exponencial
x_values = decade_count.index
y_values = decade_count.values
# Ajuste de uma curva exponencial aos dados
log_y_values = np.log(y_values + 1) # Evitando log de zero
z = np.polyfit(x_values, log_y_values, 1) # Regressão linear sobre o log dos valores
p = np.poly1d(z)
# Função exponencial para a linha de tendência
exp_fit = np.exp(p(x_values))
# Adicionando a linha de tendência ao gráfico
fig.add_traces(go.Scatter(x=x_values, y=exp_fit, mode='lines', name='Tendência Exponencial', line=dict(color='red')))
# Ajustando o layout do gráfico
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
# Exibindo o gráfico
fig.show()
Análise da Distribuição de Filmes por Década¶
Tendência de crescimento até 2000: Observamos um crescimento constante na produção de filmes desde as décadas de 1940 até o ano 2000. A linha de tendência exponencial mostra claramente que, até 2000, a produção cinematográfica aumentou consideravelmente, com um salto significativo nas décadas de 1980 e 1990. Esse aumento pode ser associado à evolução da tecnologia, expansão dos mercados de cinema, e maior acesso a recursos de produção e distribuição global.
Crescimento desacelerado de 1990 para 2000: Embora o número de filmes produzidos em 2000 seja maior do que na década de 1990, o crescimento de 1990 para 2000 não foi tão acentuado quanto nos períodos anteriores. Esse comportamento pode indicar que a produção cinematográfica global atingiu um certo patamar de saturação, onde a infraestrutura, custos de produção ou o mercado em si chegaram a um ponto de estabilidade. Outra possibilidade é o início da transição para outras formas de entretenimento, como a popularização de séries de TV e novos formatos digitais.
Produção parcial em 2010 (até 2016): Em 2000, foram produzidos 2533 filmes, enquanto até 2016, foram produzidos 1369 filmes. Como estamos analisando apenas parte da década de 2010, essa queda pode ser explicada pelo fato de que o período analisado ainda não estava completo. Entretanto, essa redução pode levantar questões sobre possíveis mudanças no comportamento de consumo, como o aumento de plataformas de streaming, que podem ter deslocado parte da produção cinematográfica tradicional para novos modelos de mídia. Outro ponto relevante é o avanço de novas tecnologias e o comportamento do mercado após crises econômicas globais, o que pode ter impactado a quantidade de produções.
Em resumo, o gráfico nos dá uma visão clara do crescimento acelerado na produção de filmes até 2000, seguido por uma desaceleração no crescimento e, por fim, uma queda parcial de produções na década de 2010, o que pode estar relacionado a fatores de transição tecnológica e novas demandas de mercado.
fig = px.box(movies_metadata, x='decade', y='vote_average',
labels={'year': 'Ano', 'vote_average': 'Nota Média'},
title='Distribuição das Notas dos Filmes por Ano')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
Insight sobre a Distribuição das Notas dos Filmes por Década¶
O gráfico revela que as notas dos filmes permaneceram relativamente estáveis ao longo das décadas e parecem tender a se estabilizar, com a mediana se mantendo entre 6 e 7. No entanto, a partir de 1980, há um aumento na dispersão das notas, com mais filmes recebendo tanto notas muito altas quanto muito baixas, refletindo a maior produção cinematográfica. As décadas anteriores a 1940 mostram uma distribuição mais concentrada, com menos outliers, sugerindo uma produção mais controlada e uniforme em termos de qualidade percebida. Essa análise mostra que, apesar da expansão do mercado, a percepção geral da qualidade dos filmes tem se mantido constante.
movies_metadata['genres'] = movies_metadata['genres'].apply(
lambda x: [d['name'] for d in eval(x)] if pd.notnull(x) else []
)
genres_df = movies_metadata.explode('genres')
genre_decade_count = genres_df.groupby(['decade', 'genres']).size().reset_index(name='film_count')
fig = px.bar(genre_decade_count, x='decade', y='film_count', color='genres',
labels={'decade': 'Década', 'film_count': 'Número de Filmes', 'genres': 'Gênero'},
title='Popularidade dos Gêneros de Filmes por Década')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
def analyze_genre_marketshare_trends(genre_decade_count):
# Primeiro, criamos um dicionário para armazenar as informações sobre cada gênero
genre_analysis = {}
# Calculando o total de filmes por década
total_per_decade = genre_decade_count.groupby('decade')['film_count'].sum()
# Para cada gênero, calculamos a participação de mercado ao longo do tempo
for genre in genre_decade_count['genres'].unique():
# Filtrar os dados por gênero
genre_data = genre_decade_count[genre_decade_count['genres'] == genre].sort_values(by='decade')
# Se tivermos mais de uma década para esse gênero, calculamos a tendência de market share
if len(genre_data) > 1:
# Calculando o market share para cada década (filmes do gênero / total de filmes na década)
genre_data = genre_data.merge(total_per_decade, on='decade', suffixes=('', '_total'))
genre_data['market_share'] = genre_data['film_count'] / genre_data['film_count_total']
# Calcula a diferença do market share entre as décadas
genre_data['market_share_diff'] = genre_data['market_share'].diff().fillna(0)
total_market_share_growth = genre_data['market_share_diff'].sum()
# Analisando a variação percentual do market share entre o começo e o fim
initial_market_share = genre_data['market_share'].iloc[0]
final_market_share = genre_data['market_share'].iloc[-1]
initial_market_share_decade = genre_data['decade'].iloc[0]
final_market_share_decade = genre_data['decade'].iloc[-1]
growth_rate_market_share = (final_market_share - initial_market_share) / initial_market_share if initial_market_share != 0 else np.nan
# Calculando o market share máximo, mínimo e as respectivas décadas
max_market_share = genre_data['market_share'].max()
max_market_share_decade = genre_data.loc[genre_data['market_share'].idxmax(), 'decade']
min_market_share = genre_data['market_share'].min()
min_market_share_decade = genre_data.loc[genre_data['market_share'].idxmin(), 'decade']
# Adicionando a análise ao dicionário
genre_analysis[genre] = {
'total_market_share_growth': total_market_share_growth,
'growth_rate_market_share': growth_rate_market_share,
'initial_market_share': initial_market_share,
'final_market_share': final_market_share,
'initial_market_share_decade': initial_market_share_decade,
'final_market_share_decade': final_market_share_decade,
'max_market_share': max_market_share,
'max_market_share_decade': max_market_share_decade,
'min_market_share': min_market_share,
'min_market_share_decade': min_market_share_decade,
'trend': 'stable' if abs(growth_rate_market_share) < 0.2 else ('growing' if growth_rate_market_share > 0 else 'declining')
}
# Ordenar os gêneros pelo 'growth_rate_market_share' em ordem decrescente
sorted_genre_analysis = dict(sorted(genre_analysis.items(), key=lambda x: x[1]['growth_rate_market_share'], reverse=True))
return sorted_genre_analysis
# Exemplo de uso da função
genre_decade_count = genres_df.groupby(['decade', 'genres']).size().reset_index(name='film_count')
genre_trends = analyze_genre_marketshare_trends(genre_decade_count)
# Exibindo as informações de tendência de market share ordenadas pelo crescimento
for genre, info in genre_trends.items():
print(f"Gênero: {genre}")
print(f" Tendência: {info['trend']}")
print(f" Crescimento total de Market Share: {info['total_market_share_growth']:.4f}")
# Exibir o market share inicial
print(f" Market Share Inicial: {info['initial_market_share']:.4f} (Década: {info['initial_market_share_decade']})")
# Exibir o market share mínimo ou máximo dependendo da ordem das décadas
if info['min_market_share_decade'] > info['initial_market_share_decade']:
print(f" Market Share Mínimo: {info['min_market_share']:.4f} (Década: {info['min_market_share_decade']})")
if info['max_market_share_decade'] > info['initial_market_share_decade']:
print(f" Market Share Máximo: {info['max_market_share']:.4f} (Década: {info['max_market_share_decade']})")
# Exibir o market share final
print(f" Market Share Final: {info['final_market_share']:.4f} (Década: {info['final_market_share_decade']})\n")
Gênero: Animation Tendência: growing Crescimento total de Market Share: 0.0207 Market Share Inicial: 0.0077 (Década: 1920.0) Market Share Mínimo: 0.0056 (Década: 1930.0) Market Share Máximo: 0.0284 (Década: 2010.0) Market Share Final: 0.0284 (Década: 2010.0) Gênero: Thriller Tendência: growing Crescimento total de Market Share: 0.0572 Market Share Inicial: 0.0462 (Década: 1920.0) Market Share Mínimo: 0.0362 (Década: 1930.0) Market Share Máximo: 0.1034 (Década: 2010.0) Market Share Final: 0.1034 (Década: 2010.0) Gênero: Family Tendência: growing Crescimento total de Market Share: 0.0162 Market Share Inicial: 0.0154 (Década: 1920.0) Market Share Máximo: 0.0669 (Década: 1930.0) Market Share Final: 0.0315 (Década: 2010.0) Gênero: TV Movie Tendência: growing Crescimento total de Market Share: 0.0009 Market Share Inicial: 0.0009 (Década: 1960.0) Market Share Mínimo: 0.0003 (Década: 1980.0) Market Share Máximo: 0.0023 (Década: 2000.0) Market Share Final: 0.0019 (Década: 2010.0) Gênero: Mystery Tendência: growing Crescimento total de Market Share: 0.0108 Market Share Inicial: 0.0154 (Década: 1920.0) Market Share Máximo: 0.0605 (Década: 1940.0) Market Share Final: 0.0262 (Década: 2010.0) Gênero: Crime Tendência: growing Crescimento total de Market Share: 0.0208 Market Share Inicial: 0.0308 (Década: 1920.0) Market Share Máximo: 0.0701 (Década: 1940.0) Market Share Final: 0.0515 (Década: 2010.0) Gênero: Documentary Tendência: growing Crescimento total de Market Share: 0.0153 Market Share Inicial: 0.0231 (Década: 1920.0) Market Share Mínimo: 0.0024 (Década: 1950.0) Market Share Máximo: 0.0384 (Década: 2010.0) Market Share Final: 0.0384 (Década: 2010.0) Gênero: Music Tendência: growing Crescimento total de Market Share: 0.0042 Market Share Inicial: 0.0077 (Década: 1920.0) Market Share Máximo: 0.0557 (Década: 1930.0) Market Share Final: 0.0119 (Década: 2010.0) Gênero: Action Tendência: growing Crescimento total de Market Share: 0.0202 Market Share Inicial: 0.0769 (Década: 1910.0) Market Share Mínimo: 0.0318 (Década: 1940.0) Market Share Máximo: 0.0971 (Década: 2010.0) Market Share Final: 0.0971 (Década: 2010.0) Gênero: Romance Tendência: stable Crescimento total de Market Share: -0.0080 Market Share Inicial: 0.0692 (Década: 1920.0) Market Share Mínimo: 0.0573 (Década: 1970.0) Market Share Máximo: 0.1393 (Década: 1930.0) Market Share Final: 0.0612 (Década: 2010.0) Gênero: Horror Tendência: declining Crescimento total de Market Share: -0.0136 Market Share Inicial: 0.0538 (Década: 1920.0) Market Share Mínimo: 0.0304 (Década: 1990.0) Market Share Máximo: 0.0683 (Década: 1980.0) Market Share Final: 0.0403 (Década: 2010.0) Gênero: Comedy Tendência: declining Crescimento total de Market Share: -0.0812 Market Share Inicial: 0.2308 (Década: 1910.0) Market Share Mínimo: 0.0875 (Década: 1950.0) Market Share Final: 0.1496 (Década: 2010.0) Gênero: Drama Tendência: declining Crescimento total de Market Share: -0.1166 Market Share Inicial: 0.3077 (Década: 1910.0) Market Share Mínimo: 0.1825 (Década: 1980.0) Market Share Final: 0.1911 (Década: 2010.0) Gênero: Foreign Tendência: declining Crescimento total de Market Share: -0.0025 Market Share Inicial: 0.0037 (Década: 1960.0) Market Share Mínimo: 0.0012 (Década: 2010.0) Market Share Máximo: 0.0070 (Década: 1990.0) Market Share Final: 0.0012 (Década: 2010.0) Gênero: Adventure Tendência: declining Crescimento total de Market Share: -0.2718 Market Share Inicial: 0.3333 (Década: 1900.0) Market Share Mínimo: 0.0239 (Década: 1940.0) Market Share Final: 0.0615 (Década: 2010.0) Gênero: History Tendência: declining Crescimento total de Market Share: -0.0657 Market Share Inicial: 0.0769 (Década: 1910.0) Market Share Mínimo: 0.0112 (Década: 2010.0) Market Share Final: 0.0112 (Década: 2010.0) Gênero: Science Fiction Tendência: declining Crescimento total de Market Share: -0.2859 Market Share Inicial: 0.3333 (Década: 1900.0) Market Share Mínimo: 0.0096 (Década: 1940.0) Market Share Final: 0.0475 (Década: 2010.0) Gênero: Fantasy Tendência: declining Crescimento total de Market Share: -0.2990 Market Share Inicial: 0.3333 (Década: 1900.0) Market Share Mínimo: 0.0195 (Década: 1930.0) Market Share Final: 0.0344 (Década: 2010.0) Gênero: War Tendência: declining Crescimento total de Market Share: -0.0704 Market Share Inicial: 0.0769 (Década: 1910.0) Market Share Mínimo: 0.0066 (Década: 2010.0) Market Share Final: 0.0066 (Década: 2010.0) Gênero: Western Tendência: declining Crescimento total de Market Share: -0.0719 Market Share Inicial: 0.0769 (Década: 1910.0) Market Share Mínimo: 0.0026 (Década: 2000.0) Market Share Final: 0.0050 (Década: 2010.0)
genre_count = genres_df['genres'].value_counts().reset_index()
genre_count.columns = ['genre', 'film_count']
fig = px.bar(genre_count, x='genre', y='film_count',
labels={'genre': 'Gênero', 'film_count': 'Número de Filmes'},
title='Número de Filmes por Gênero')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
average_budget_per_year = movies_metadata.groupby('year')['budget'].mean().reset_index()
fig = px.line(average_budget_per_year, x='year', y='budget',
labels={'year': 'Ano', 'budget': 'Orçamento Médio (USD)'},
title='Evolução dos Orçamentos Médios dos Filmes por Ano')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
# Garantindo que a coluna 'budget' é numérica
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
# Filtrando os filmes de 1927
movies_1927 = movies_metadata[movies_metadata['year'] == 1927]
# Verificando o orçamento médio de 1927
budget_1927_avg = movies_1927['budget'].mean()
print(f"Orçamento médio dos filmes em 1927: ${budget_1927_avg:.2f}")
# Exibindo os 5 filmes com os maiores orçamentos de 1927
top_budget_1927 = movies_1927[['title', 'budget', 'genres', 'production_countries', 'production_companies']].sort_values(by='budget', ascending=False).head()
print("Top 5 filmes com maiores orçamentos em 1927:")
print(top_budget_1927)
# Analisando a receita (caso disponível)
if 'revenue' in movies_metadata.columns:
revenue_1927 = movies_1927['revenue'].mean()
print(f"Receita média dos filmes de 1927: ${revenue_1927:.2f}")
# Identificando filmes específicos que possam ter influenciado esse aumento
# Investigando outliers de orçamento
outliers_1927 = movies_1927[movies_1927['budget'] > 1.5 * budget_1927_avg]
print("Filmes com orçamentos excepcionalmente altos em 1927:")
print(outliers_1927[['title', 'budget', 'genres', 'production_companies']])
# Identificando o país e estúdios dos filmes
country_count_1927 = movies_1927['production_countries'].value_counts()
print("\nPaíses de produção mais comuns em 1927:")
print(country_count_1927)
studio_count_1927 = movies_1927['production_companies'].value_counts()
print("\nEstúdios de produção mais comuns em 1927:")
print(studio_count_1927)
Orçamento médio dos filmes em 1927: $31544000.00
Top 5 filmes com maiores orçamentos em 1927:
title budget \
1568 Metropolis 92620000.0
1484 Wings 2000000.0
1758 The Lodger: A Story of the London Fog 12000.0
5264 Berlin: Symphony of a Great City NaN
5323 Sunrise: A Song of Two Humans NaN
genres \
1568 [Drama, Science Fiction]
1484 [Action, Drama, Romance, War]
1758 [Crime, Drama, Thriller]
5264 [Documentary, History]
5323 [Drama, Romance]
production_countries \
1568 [{'iso_3166_1': 'DE', 'name': 'Germany'}]
1484 [{'iso_3166_1': 'US', 'name': 'United States o...
1758 [{'iso_3166_1': 'GB', 'name': 'United Kingdom'}]
5264 [{'iso_3166_1': 'DE', 'name': 'Germany'}]
5323 [{'iso_3166_1': 'US', 'name': 'United States o...
production_companies
1568 [{'name': 'Paramount Pictures', 'id': 4}, {'na...
1484 [{'name': 'Paramount Famous Lasky Corporation'...
1758 [{'name': 'Carlyle Blackwell Productions', 'id...
5264 [{'name': 'Deutsche Vereinsfilm AG', 'id': 50}...
5323 [{'name': 'BIM Distribuzione', 'id': 225}, {'n...
Receita média dos filmes de 1927: $325271.50
Filmes com orçamentos excepcionalmente altos em 1927:
title budget genres \
1568 Metropolis 92620000.0 [Drama, Science Fiction]
production_companies
1568 [{'name': 'Paramount Pictures', 'id': 4}, {'na...
Países de produção mais comuns em 1927:
production_countries
[{'iso_3166_1': 'US', 'name': 'United States of America'}] 4
[{'iso_3166_1': 'DE', 'name': 'Germany'}] 2
[{'iso_3166_1': 'GB', 'name': 'United Kingdom'}] 1
[{'iso_3166_1': 'FR', 'name': 'France'}] 1
Name: count, dtype: int64
Estúdios de produção mais comuns em 1927:
production_companies
[] 2
[{'name': 'Paramount Famous Lasky Corporation', 'id': 33333}] 1
[{'name': 'Paramount Pictures', 'id': 4}, {'name': 'Universum Film (UFA)', 'id': 12372}] 1
[{'name': 'Carlyle Blackwell Productions', 'id': 1222}] 1
[{'name': 'Deutsche Vereinsfilm AG', 'id': 50}, {'name': 'Fox Europa Produktion', 'id': 51}] 1
[{'name': 'BIM Distribuzione', 'id': 225}, {'name': 'Fox Film Corporation', 'id': 5488}] 1
[{'name': 'Pathé Consortium Cinéma', 'id': 220}, {'name': 'Société générale des films', 'id': 1237}, {'name': 'Films Abel Gance', 'id': 9005}, {'name': 'Isepa-Wengeroff Film GmbH', 'id': 9006}, {'name': 'Ciné France', 'id': 27693}, {'name': 'Société Westi', 'id': 82280}] 1
Name: count, dtype: int64
movies_1927 = movies_metadata[movies_metadata['year'] == 1927]
movies_1927_sorted = movies_1927[['title', 'budget']].sort_values(by='budget', ascending=False)
# Plotando o gráfico de colunas verticais
plt.figure(figsize=(10, 6))
plt.bar(movies_1927_sorted['title'], movies_1927_sorted['budget'], color='skyblue')
plt.xlabel('Título', fontsize=12)
plt.ylabel('Orçamento (USD)', fontsize=12)
plt.title('Orçamento dos Filmes de 1927', fontsize=14)
plt.xticks(rotation=45, ha='right', fontsize=10) # Girar os títulos para melhor visualização
plt.tight_layout()
# Mostrar o gráfico
plt.show()
c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning: backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered. c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning: backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered. c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning: backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered.
Análise da Evolução dos Orçamentos Médios dos Filmes por Ano¶
O gráfico acima mostra a evolução dos orçamentos médios dos filmes ao longo dos anos, em dólares americanos. Podemos observar que, até a década de 1960, os orçamentos dos filmes mantiveram-se relativamente estáveis, com uma tendência de crescimento mais acentuada a partir da década de 1980.
Destaque: 1927 como um Outlier¶
Um ponto notável é o pico em 1927, que se destaca como um outlier significativo. Esse aumento drástico no orçamento médio é explicado pelo fato de que, nesse ano, apenas três filmes tiveram seus orçamentos registrados. Um dos filmes mais importantes desse período foi Metropolis, uma das maiores produções cinematográficas da época, conhecida por seu orçamento extraordinariamente alto para os padrões da época.
fig = px.scatter(movies_metadata, x='budget', y='vote_average',
labels={'budget': 'Orçamento (USD)', 'vote_average': 'Nota Média'},
title='Relação entre Orçamento e Nota Média dos Filmes')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
Análise da Relação entre Orçamento e Nota Média dos Filmes¶
O gráfico acima ilustra a relação entre o orçamento (em dólares) e a nota média dos filmes. Podemos observar algumas tendências interessantes ao analisar esses dados:
Observações Principais:¶
Forma de Funil:
- À medida que o orçamento dos filmes aumenta, a dispersão das notas médias parece se afunilar. Isso significa que, embora filmes de baixo orçamento possam ter notas muito variadas (de notas muito baixas até notas muito altas), filmes de orçamento mais elevado tendem a ter suas notas concentradas em uma faixa mais estreita, geralmente entre 5 e 8.
Maior Estabilidade em Filmes de Alto Orçamento:
- Filmes com orçamentos mais altos, especialmente aqueles com mais de 200 milhões de dólares, mostram uma menor variabilidade nas notas médias. Esses filmes tendem a se manter na média, com poucas chances de obterem notas muito baixas ou muito altas. Isso indica que, embora o alto investimento não garanta uma nota extremamente alta, ele minimiza o risco de fracasso crítico.
Alta Variabilidade em Filmes de Baixo Orçamento:
- Por outro lado, filmes de baixo orçamento (menos de 50 milhões de dólares) apresentam uma variabilidade significativa nas notas, com exemplos que vão de grandes sucessos críticos (notas acima de 8) até fracassos (notas abaixo de 3).
Hipótese:¶
Com base na forma de funil observada no gráfico, podemos levantar a seguinte hipótese: filmes de alto orçamento tendem a ser projetos mais "seguros" em termos de qualidade percebida, o que resulta em notas médias mais previsíveis. Isso pode ocorrer porque grandes estúdios com altos orçamentos têm mais recursos para investir em equipes experientes, efeitos especiais de ponta e campanhas de marketing, o que contribui para garantir um padrão de qualidade mais consistente, evitando notas muito baixas. No entanto, para garantir o resultado do filme, o lado criativo dos filmes pode acabar sendo deixado de lado e, ao tomar menos riscos, a possibilidade de notas a cima ou abaixo da média tende a cair.
Por outro lado, filmes de baixo orçamento parecem representar apostas mais arriscadas, onde os resultados podem variar amplamente. Nessa faixa, projetos inovadores ou independentes podem alcançar grande sucesso crítico, enquanto filmes com menos recursos ou apelo comercial podem ser mal avaliados.
movies_metadata['countries'] = movies_metadata['production_countries'].apply(
lambda x: [d['name'] for d in eval(x)] if pd.notnull(x) else []
)
countries_df = movies_metadata.explode('countries')
country_counts = countries_df['countries'].value_counts().reset_index()
country_counts.columns = ['country', 'film_count']
fig = px.choropleth(country_counts,
locations="country",
locationmode='country names',
color="film_count",
hover_name="country",
color_continuous_scale=px.colors.sequential.Reds,
labels={'film_count': 'Número de Filmes'},
title="Número de Filmes por País")
fig.update_layout(
title_font_size=20,
geo=dict(
showframe=False,
showcoastlines=False,
projection_type='natural earth',
showland=True,
landcolor='white',
showlakes=True,
lakecolor='lightblue',
subunitwidth=0.5,
countrywidth=0.5
),
margin={"r":0,"t":50,"l":0,"b":0},
)
fig.show()
country_counts
| country | film_count | |
|---|---|---|
| 0 | United States of America | 6847 |
| 1 | United Kingdom | 1205 |
| 2 | France | 698 |
| 3 | Germany | 541 |
| 4 | Canada | 359 |
| ... | ... | ... |
| 90 | Liechtenstein | 1 |
| 91 | Czechoslovakia | 1 |
| 92 | Cameroon | 1 |
| 93 | Nepal | 1 |
| 94 | Sri Lanka | 1 |
95 rows × 2 columns
movies_metadata_filtered = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] > 0)]
fig = px.scatter(movies_metadata_filtered, x='budget', y='revenue',
labels={'budget': 'Orçamento (USD)', 'revenue': 'Receita (USD)'},
title='Orçamento vs Receita')
fig.update_xaxes(type="log", matches='y')
fig.update_yaxes(type="log", matches='x')
fig.add_shape(
type="line",
x0=min(movies_metadata_filtered['budget']),
y0=min(movies_metadata_filtered['budget']),
x1=max(movies_metadata_filtered['budget']),
y1=max(movies_metadata_filtered['budget']),
line=dict(color="red", width=2, dash="dash"),
)
fig.update_layout(
title_font_size=16,
xaxis_title_font_size=14,
yaxis_title_font_size=14,
width=800,
height=600,
showlegend=False
)
fig.show()
Análise do Gráfico de Orçamento vs Receita¶
O gráfico acima mostra a relação entre o orçamento (em USD) e a receita dos filmes. Podemos observar uma correlação positiva clara entre os dois, o que indica que, em geral, filmes com maior orçamento tendem a gerar maior receita. A linha pontilhada em vermelho indica essa tendência um filme que teve a receita igual ao orçamento, e como podemos ver, a maioria dos filmes ficam a cima dessa linha. Além dissom as chances de se estar a cima dessa linha cresciem conforme o seu orçamento é maior.
print(movies_metadata['genres'].head())
0 [Animation, Comedy, Family] 1 [Adventure, Fantasy, Family] 2 [Romance, Comedy] 3 [Comedy, Drama, Romance] 4 [Comedy] Name: genres, dtype: object
from itertools import combinations
movies_metadata = movies_metadata[movies_metadata['genres'].apply(len) > 0]
genre_combinations = movies_metadata['genres'].apply(lambda x: list(combinations(x, 2)))
flat_combinations = [item for sublist in genre_combinations for item in sublist]
cooccurrence_matrix = pd.DataFrame(flat_combinations, columns=['Genre1', 'Genre2'])
cooccurrence_matrix['Cooccurrence'] = 1
cooccurrence_pivot = cooccurrence_matrix.pivot_table(index='Genre1', columns='Genre2', values='Cooccurrence', aggfunc='sum', fill_value=0)
genre_counts = movies_metadata['genres'].explode().value_counts()
for genre in genre_counts.index:
cooccurrence_pivot.loc[genre, genre] = genre_counts[genre]
for genre in cooccurrence_pivot.index:
cooccurrence_pivot.loc[genre] = cooccurrence_pivot.loc[genre] / genre_counts[genre]
for genre in cooccurrence_pivot.index:
if genre not in cooccurrence_pivot.columns:
cooccurrence_pivot[genre] = 0
for genre in cooccurrence_pivot.columns:
if genre not in cooccurrence_pivot.index:
cooccurrence_pivot.loc[genre] = 0
cooccurrence_pivot = cooccurrence_pivot.sort_index(axis=0).sort_index(axis=1)
fig = px.imshow(cooccurrence_pivot,
labels=dict(x="Gênero", y="Gênero", color="Proporção de Coocorrência"),
x=cooccurrence_pivot.columns,
y=cooccurrence_pivot.index,
color_continuous_scale="Blues",
title="Matriz de Coocorrência Normalizada de Gêneros de Filmes")
fig.update_layout(
title_font_size=20,
xaxis_title_font_size=16,
yaxis_title_font_size=16,
width=1000,
height=1000,
xaxis=dict(tickangle=-45),
)
fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.27837992013690815' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.027381631488876214' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.23445521962350258' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.2316029663434113' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.0017113519680547634' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.24472333143183114' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05989731888191671' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05932686822589846' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.005704506560182544' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.03479749001711352' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.037079292641186534' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.008556759840273816' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.04677695379349686' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05932686822589846' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.1791215059897319' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.41186537364517967' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.03993154592127781' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.034227039361095266' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.17042606516290726' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.002506265664160401' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
# Extraindo os pares de coocorrência e seus valores resultantes em um DataFrame
cooccurrence_flat = cooccurrence_pivot.stack().reset_index()
cooccurrence_flat.columns = ['Genre1', 'Genre2', 'Cooccurrence Value']
# Filtrando os pares com valor > 0 e onde Genre1 e Genre2 são diferentes, e ordenando pelos valores de coocorrência em ordem decrescente
cooccurrence_flat_sorted = cooccurrence_flat[
(cooccurrence_flat['Cooccurrence Value'] > 0) & (cooccurrence_flat['Genre1'] != cooccurrence_flat['Genre2'])
].sort_values(by='Cooccurrence Value', ascending=False)
# Exibindo os resultados ordenados
print(cooccurrence_flat_sorted)
Genre1 Genre2 Cooccurrence Value 47 Animation Family 0.559091 277 Mystery Thriller 0.430108 86 Crime Drama 0.419124 237 Horror Thriller 0.417666 17 Action Thriller 0.411865 .. ... ... ... 296 Romance TV Movie 0.001082 342 Thriller Animation 0.000992 345 Thriller Documentary 0.000992 29 Adventure Foreign 0.000835 356 Thriller TV Movie 0.000496 [336 rows x 3 columns]
Análise da Matriz de Coocorrência de Gêneros de Filmes¶
A matriz de coocorrência apresentada foi construída para mostrar a proporção de vezes que dois gêneros de filmes aparecem juntos. Para cada par de gêneros, o valor na matriz representa a frequência relativa com que os dois aparecem no mesmo filme, normalizada pela quantidade total de filmes de cada gênero.
Interpretação do Resultado:¶
Gêneros com Alta Coocorrência: Gêneros com valores de coocorrência mais altos (próximos de 1) indicam que esses dois gêneros aparecem frequentemente juntos. Isso sugere que há uma correlação forte entre esses gêneros, ou seja, os filmes que pertencem a um desses gêneros tendem a pertencer também ao outro.
Gêneros com Baixa Coocorrência: Gêneros com valores mais baixos indicam que esses dois gêneros aparecem juntos com menos frequência. Quando o valor de coocorrência é 0, significa que não há filmes que pertencem a ambos os gêneros ao mesmo tempo.
Exemplos de Análise:¶
Se o par de gêneros "Ação" e "Aventura" tiver um valor de coocorrência alto, isso sugere que filmes de Ação frequentemente também são classificados como Aventura, o que faz sentido dado que esses gêneros muitas vezes compartilham temas similares.
Por outro lado, se o par "Documentário" e "Comédia" tiver um valor de coocorrência muito baixo ou nulo, isso indica que é raro encontrar filmes que pertençam simultaneamente a esses dois gêneros, pois eles têm temas e características bastante diferentes.
Aplicabilidade:¶
Essa análise pode ser utilizada para entender melhor padrões de combinação de gêneros em filmes e pode ser útil em diversas áreas, como Recomendações de filmes, para identificar gêneros que frequentemente aparecem juntos pode ajudar a sugerir filmes de gêneros correlacionados.
correlation_matrix = movies_metadata[['budget', 'revenue', 'runtime', 'vote_average', 'popularity', 'profit']].corr(method='pearson')
correlation_matrix = correlation_matrix.round(2)
fig = px.imshow(correlation_matrix,
text_auto=True,
color_continuous_scale='RdBu_r',
labels=dict(x="Attributes", y="Attributes", color="Correlation"),
title="Matriz de Correlação entre Atributos")
fig.update_layout(
width=800,
height=600,
xaxis_title="Atributos",
yaxis_title="Atributos"
)
fig.show()
Análise da Matriz de Correlação entre Atributos de Filmes¶
A matriz de correlação apresentada mostra a relação entre diferentes atributos de filmes, como orçamento (budget), receita (revenue), tempo de execução (runtime), nota média (vote_average), e popularidade (popularity). A correlação varia de -1 (correlação negativa perfeita) a 1 (correlação positiva perfeita), onde valores próximos de 0 indicam pouca ou nenhuma correlação.
Principais Insights:¶
Forte Correlação entre Orçamento e Receita (0.72):
- Existe uma correlação forte e positiva entre o orçamento e a receita. Isso indica que, em geral, filmes com orçamentos mais altos tendem a gerar receitas maiores. Esta é uma tendência comum na indústria cinematográfica, onde produções maiores têm maior capacidade de atrair público e gerar retornos significativos.
Correlação entre Popularidade e Receita (0.43):
- A popularidade também tem uma correlação moderada com a receita (0.43), sugerindo que filmes mais populares conseguem gerar receitas mais altas. No entanto, essa correlação não é tão alta quanto a de orçamento e receita, o que implica que, mesmo com grande popularidade, fatores como orçamento e distribuição podem ter um peso maior no sucesso financeiro.
Correlação entre Tempo de Execução e Orçamento (0.22):
- Há uma correlação positiva moderada entre o tempo de execução (runtime) e o orçamento. Filmes mais longos tendem a ter mais orçamento, o que pode estar relacionado ao fato de que filmes de maior duração frequentemente são grandes produções.
Baixa Correlação entre Nota Média e Orçamento (-0.08):
- A correlação entre orçamento e nota média (vote_average) é praticamente nula e até ligeiramente negativa. Isso significa que gastar mais dinheiro em um filme não garante que ele será bem avaliado pela crítica ou pelo público. De fato, muitos filmes de grande orçamento podem ter foco em aspectos comerciais, enquanto filmes de baixo orçamento podem alcançar grande sucesso crítico.
Popularidade e Orçamento (0.27):
- A popularidade está moderadamente correlacionada com o orçamento. Isso indica que filmes com orçamentos maiores tendem a ser mais populares, mas não é uma correlação muito forte. Isso sugere que, embora o orçamento ajude a aumentar a visibilidade do filme, a popularidade também depende de outros fatores, como que podem estar relacionados possivelmente com atores, marketing e etc.
Correlação Levemente Alta entre Orçamento e Lucro (0.57):
- Existe uma correlação positiva levemente alta entre orçamento e lucro. Isso sugere que, em geral, filmes com orçamentos mais altos têm uma tendência a gerar lucros maiores, embora não seja uma garantia absoluta.
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']
movies_metadata['release_date'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce')
movies_metadata['release_month'] = movies_metadata['release_date'].dt.month
genres_exploded = movies_metadata.explode('genres')
profit_per_genre_month = genres_exploded.groupby(['genres', 'release_month'])['profit'].mean().reset_index()
fig = px.line(profit_per_genre_month, x='release_month', y='profit', color='genres',
title='Média de Lucro por Mês para Cada Gênero',
labels={'release_month': 'Mês de Lançamento', 'profit': 'Média de Lucro'},
markers=True)
fig.update_layout(
xaxis_title='Mês de Lançamento',
yaxis_title='Média de Lucro',
title_x=0.5,
width=900,
height=600
)
fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:3: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:4: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']
movies_metadata['release_date'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce')
movies_metadata['release_month'] = movies_metadata['release_date'].dt.month
profit_per_month = movies_metadata.groupby('release_month')['profit'].mean().reset_index()
fig = px.line(profit_per_month, x='release_month', y='profit',
title='Média de Lucro por Mês de Lançamento',
labels={'release_month': 'Mês de Lançamento', 'profit': 'Média de Lucro'},
markers=True)
fig.update_layout(
xaxis_title='Mês de Lançamento',
yaxis_title='Média de Lucro',
title_x=0.5,
width=900,
height=600
)
fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:3: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:4: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Análise da Média de Lucro por Mês de Lançamento¶
O gráfico mostra a quantidade de lucro gerada por um filme baseado no mês em que foi lançado. Os números variam entre aproximadamente 29 milhões de dólares (Janeiro) até 132 milhões de dólares (Junho).
Insight:¶
Gêneros de filme que seguem o padrão do gráfico:
- Reparamos que existem alguns gêneros de filme que seguem o padrão do gráfico, que inclui todos os gêneros de forma bem parecida, com destaque para os gêneros de família e animação. Como hipótese, temos que o período de férias escolares americanas se inicia aproximadamente na metade de maio e se estende até o fim de agosto, sendo uma grande fonte de público, principalmente para filmes de família e animação, que, comumente, atraem um público mais jovem.
- Já filmes com gêneros como horror e documentário tendem a seguir padrões de lançamento e lucro diferentes.
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')
movies_metadata = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] >= 0)].copy()
movies_metadata['is_flop'] = movies_metadata['revenue'] < movies_metadata['budget']
movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')
# 4. Count Flops per Genre
flop_movies = genres_exploded[genres_exploded['is_flop'] == True]
flop_counts = flop_movies['genres'].value_counts().reset_index()
flop_counts.columns = ['Genre', 'Number of Flops']
fig = px.bar(
flop_counts,
x='Genre',
y='Number of Flops',
title='Number of Flops per Genre',
labels={'Genre': 'Gênero', 'Number of Flops': 'Número de Flops'},
)
fig.update_traces(marker_color='blue') # Definindo a cor azul para todas as barras
fig.update_layout(
xaxis_title='Gênero',
yaxis_title='Número de Flops',
title_x=0.5,
xaxis_tickangle=-45,
width=900,
height=600
)
fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3357064704.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3357064704.py:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
fig = px.bar(genre_count, x='genre', y='film_count',
labels={'genre': 'Gênero', 'film_count': 'Número de Filmes'},
title='Número de Filmes por Gênero')
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)
fig.show()
Nenhuma informação pode ser concluida a partir do número de flops, tendo em vista que ele está muito relacionado com o número de filmes feitos.
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')
movies_metadata = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] >= 0)].copy()
movies_metadata['is_flop'] = movies_metadata['revenue'] < movies_metadata['budget']
movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')
total_movies_per_genre = genres_exploded['genres'].value_counts().reset_index()
total_movies_per_genre.columns = ['Genre', 'Total Movies']
flop_movies = genres_exploded[genres_exploded['is_flop'] == True]
flop_counts = flop_movies['genres'].value_counts().reset_index()
flop_counts.columns = ['Genre', 'Number of Flops']
flop_proportion = flop_counts.merge(total_movies_per_genre, on='Genre')
flop_proportion['Flop Proportion'] = flop_proportion['Number of Flops'] / flop_proportion['Total Movies']
flop_proportion = flop_proportion.sort_values(by='Flop Proportion', ascending=False)
fig = px.bar(
flop_proportion,
x='Genre',
y='Flop Proportion',
title='Proporção de Flops por Gênero (Ordenado do Maior para o Menor)',
labels={'Genre': 'Gênero', 'Flop Proportion': 'Proporção de Flops'},
)
fig.update_traces(marker_color='blue')
fig.update_layout(
xaxis_title='Gênero',
yaxis_title='Proporção de Flops',
title_x=0.5,
xaxis_tickangle=-45,
width=900,
height=600
)
fig.show()
Proporção de Flops por Gênero¶
Apesar do gráfico apresentar as proporções de filmes que floppam em cada gênero, não é possível tirar conclusões definitivas sobre a relação entre o gênero de um filme e a probabilidade de ele floppar. Há muitos fatores envolvidos no desempenho financeiro de um filme, como o orçamento, marketing, recepção crítica, concorrência no mercado, entre outros. Portanto, não se pode inferir que determinados gêneros estão mais propensos a floppar apenas com base nesses dados.
Seria necessário uma análise mais profunda, considerando outros fatores que influenciam o sucesso ou fracasso de um filme.
movies_metadata['is_flop'] = (movies_metadata['revenue'] < movies_metadata['budget']).astype(int)
movies_metadata['popularity'] = pd.to_numeric(movies_metadata['popularity'], errors='coerce')
# Remover filmes com valores ausentes nas métricas analisadas
movies_metadata_clean = movies_metadata.dropna(subset=['budget', 'revenue', 'vote_average', 'runtime', 'popularity'])
# Criar gráfico de coordenadas paralelas com 'is_flop' como dimensão final
fig_parallel_flop = px.parallel_coordinates(
movies_metadata_clean,
dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'is_flop'],
color='is_flop',
color_continuous_scale=px.colors.diverging.Tealrose,
title="Análise de Filmes Flop vs Não Flop em Coordenadas Paralelas (com Flop)",
labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'is_flop': 'É Flop?'}
)
# Mostrar o gráfico de coordenadas paralelas atualizado
fig_parallel_flop.show()
# Definir o número de grupos
num_grupos = 100
# Criar uma função para agrupar os dados por flop e não flop, e calcular médias para cada subgrupo
def gerar_grupos_aleatorios(df, num_grupos):
# Selecionar apenas as colunas numéricas para evitar erros
numeric_cols = df.select_dtypes(include=[np.number])
# Separar filmes flops e não flops
flop = numeric_cols[df['is_flop'] == 1]
non_flop = numeric_cols[df['is_flop'] == 0]
# Dividir os dados de flop e não flop em partes iguais
flop_grupos = np.array_split(flop, num_grupos // 2)
non_flop_grupos = np.array_split(non_flop, num_grupos // 2)
# Lista para armazenar os grupos
grupos_lista = []
# Calcular a média para cada subgrupo de flop e não flop
for flop_grupo in flop_grupos:
grupo_media = flop_grupo.mean().to_frame().T
grupo_media['is_flop'] = 1 # Marcador de flop
grupos_lista.append(grupo_media)
for non_flop_grupo in non_flop_grupos:
grupo_media = non_flop_grupo.mean().to_frame().T
grupo_media['is_flop'] = 0 # Marcador de não flop
grupos_lista.append(grupo_media)
# Concatenar todos os grupos
grupos = pd.concat(grupos_lista, ignore_index=True)
return grupos
# Gerar os grupos aleatórios com médias
movies_metadata_grupos = gerar_grupos_aleatorios(movies_metadata_clean, num_grupos)
# Criar gráfico de coordenadas paralelas com os grupos gerados
fig_grupos_parallel = px.parallel_coordinates(
movies_metadata_grupos,
dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'is_flop'],
color='is_flop',
color_continuous_scale=px.colors.diverging.Tealrose,
title=f"Análise de Filmes Flop vs Não Flop (Médias de {num_grupos} Grupos)",
labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'is_flop': 'É Flop?'}
)
# Mostrar o gráfico com grupos de médias
fig_grupos_parallel.show()
c:\Users\Eric\anaconda3\Lib\site-packages\numpy\core\fromnumeric.py:59: FutureWarning: 'DataFrame.swapaxes' is deprecated and will be removed in a future version. Please use 'DataFrame.transpose' instead.
Insights Principais:¶
Orçamento e Receita:
- Filmes classificados como "não flop" apresentam receitas significativamente maiores, muitas vezes superando os $150M, enquanto os filmes "flop" têm receitas bem inferiores, frequentemente abaixo de $50M, independentemente do orçamento. Essa relação vem da própria natureza do que é ser flop, visto que flops sõ definidos como receita < orçamento.
Média de Votos:
- Filmes que não são flop tendem a ter uma média de votos mais alta, geralmente acima de 6.2, enquanto os flops tendem a se concentrar em médias de votos abaixo de 6.0.
Duração:
- A duração dos filmes não parece ser um fator decisivo na distinção entre flop e não flop, com ambos os grupos apresentando uma distribuição similar entre 100 e 120 minutos. No entando parece que filmes como uma duração muito pequena (menos de 1 hora e meia) tendem a ser flops.
Popularidade:
- Filmes "não flop" são, em média, mais populares, com valores de popularidade mais elevados, enquanto os flops têm popularidade baixa, abaixo de 10 na maioria dos casos.
Padrões Gerais:
- As linhas que representam os grupos de filmes flop tendem a se concentrar em valores mais baixos em quase todas as dimensões (receita, orçamento, popularidade), enquanto os filmes de sucesso se destacam nessas variáveis.
# Função para remover os 5% superiores e inferiores de ROI
def remover_extremos(df, column):
lower_bound = df[column].quantile(0.1)
upper_bound = df[column].quantile(0.9)
return df[(df[column] >= lower_bound) & (df[column] <= upper_bound)]
# Remover os 5% superiores e inferiores de ROI
movies_metadata_clean = remover_extremos(movies_metadata_clean, 'ROI')
# Criar gráfico de coordenadas paralelas com 'is_flop' como dimensão final
fig_parallel_flop = px.parallel_coordinates(
movies_metadata_clean,
dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'ROI'],
color='ROI',
color_continuous_scale=px.colors.diverging.Tealrose,
title="Análise de ROI em Coordenadas Paralelas (com ROI)",
labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'ROI': 'ROI'}
)
# Mostrar o gráfico de coordenadas paralelas atualizado
fig_parallel_flop.show()
# Definir o número de grupos
num_grupos = 500
# Função para agrupar os dados por flop e não flop, e calcular médias para cada subgrupo
def gerar_grupos_aleatorios(df, num_grupos):
# Selecionar apenas as colunas numéricas para evitar erros
numeric_cols = df.select_dtypes(include=[np.number])
# Separar filmes flops (ROI < 0) e não flops (ROI >= 0)
flop = numeric_cols[df['ROI'] < 0]
non_flop = numeric_cols[df['ROI'] >= 0]
# Dividir os dados de flop e não flop em partes iguais
flop_grupos = np.array_split(flop, num_grupos // 2)
non_flop_grupos = np.array_split(non_flop, num_grupos // 2)
# Lista para armazenar os grupos
grupos_lista = []
# Calcular a média para cada subgrupo de flop e não flop
for flop_grupo in flop_grupos:
grupo_media = flop_grupo.mean().to_frame().T
grupos_lista.append(grupo_media)
for non_flop_grupo in non_flop_grupos:
grupo_media = non_flop_grupo.mean().to_frame().T
grupos_lista.append(grupo_media)
# Concatenar todos os grupos
grupos = pd.concat(grupos_lista, ignore_index=True)
return grupos
# Gerar os grupos aleatórios com médias
movies_metadata_grupos = gerar_grupos_aleatorios(movies_metadata_clean, num_grupos)
# Criar gráfico de coordenadas paralelas com os grupos gerados
fig_grupos_parallel = px.parallel_coordinates(
movies_metadata_grupos,
dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'ROI'], # Agora usando o valor real de ROI
color='ROI', # ROI como valor contínuo
color_continuous_scale=px.colors.diverging.Tealrose,
title=f"Análise de ROI (Médias de {num_grupos} Grupos, sem 5% superiores e inferiores de ROI)",
labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'ROI': 'ROI'}
)
# Mostrar o gráfico com grupos de médias
fig_grupos_parallel.show()
c:\Users\Eric\anaconda3\Lib\site-packages\numpy\core\fromnumeric.py:59: FutureWarning: 'DataFrame.swapaxes' is deprecated and will be removed in a future version. Please use 'DataFrame.transpose' instead.
Insights sobre o ROI:¶
Orçamento e Receita:
- Grupos com altos retornos sobre investimento (ROI) tendem a ter receitas significativamente maiores, chegando a mais de $600M, mesmo quando o orçamento é consideravelmente menor, indicando uma eficiência no uso do orçamento.
- Por outro lado, grupos com ROI negativo ou muito baixo geralmente possuem receitas abaixo de $100M, mesmo quando o orçamento foi elevado.
Média de Votos:
- Filmes com menor ROI também tendem a ter uma média de votos menor que 6.0, enquanto filmes com notas maiores que 6.0 estão bastante distribuidos com relação ao ROI.
Duração:
- Filmes com durações menos longas (abaixo de 95 minutos) parecem estar associados a ROI negativo, enquanto filmes mais longos apresentam uma maior variabilidade, com muitos grupos de ROI baixo e alto.
Popularidade:
- Filmes com alta popularidade, especialmente acima de 40, estão mais correlacionados com ROI positivo, enquanto grupos com ROI negativo tendem a ter popularidade abaixo de 10.
Parece que é simples encontras características que identificam que um ROI nãp é alto, no entanto é difícil entender as características que fazem um ROI ser alto.
#Ensure 'budget' is numeric
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
# Remove movies with missing or zero budget
movies_metadata = movies_metadata[movies_metadata['budget'] > 0].copy()
#Explode Genres (Each genre gets its own row)
movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')
#Group by genre and calculate the average budget
average_budget_per_genre = genres_exploded.groupby('genres')['budget'].mean().reset_index()
#Sort by average budget in descending order
average_budget_per_genre = average_budget_per_genre.sort_values(by='budget', ascending=False)
#Visualize the average budget per genre
fig = px.bar(
average_budget_per_genre,
x='genres',
y='budget',
title='Genres with the Highest Average Budget',
labels={'genres': 'Gênero', 'budget': 'Orçamento Médio (USD)'},
)
# Setting all bars to the same color
fig.update_traces(marker_color='blue')
# Adjusting layout
fig.update_layout(
xaxis_title='Gênero',
yaxis_title='Orçamento Médio (USD)',
title_x=0.5,
xaxis_tickangle=-45,
width=900,
height=600
)
fig.show()
Análise do Gráfico de Orçamento Médio por Gênero¶
O gráfico acima mostra o orçamento médio (em dólares) dos filmes, separados por gênero. Aqui estão algumas conclusões possíveis a partir dos dados apresentados:
Principais Insights:¶
Gêneros com Maior Orçamento Médio:
- Animação e Aventura lideram como os gêneros com o maior orçamento médio, ambos ultrapassando os 60 milhões de dólares. Isso faz sentido, já que filmes de animação frequentemente envolvem grandes equipes de artistas, longos períodos de produção e tecnologias avançadas para criar visuais detalhados. Filmes de aventura, por sua vez, geralmente incluem cenas de ação extensas, efeitos especiais e gravações em locais variados, o que aumenta os custos de produção.
Gêneros com Orçamento Intermediário:
- Gêneros como Thriller, War, History, e Mystery têm orçamentos médios na faixa de 30 a 40 milhões de dólares. Estes gêneros tendem a ter menos dependência de efeitos especiais caros, mas ainda requerem orçamentos substanciais para cenários, atores e, em alguns casos, recriação histórica ou cenas de guerra.
Gêneros com Orçamento Baixo:
- Gêneros como Documentário, e Filmes Estrangeiros (Foreign) estão entre os que têm menores orçamentos médios, com valores abaixo de 20 milhões de dólares. Esses gêneros frequentemente envolvem menos dependência de efeitos visuais e ação, e podem ser produzidos com custos mais baixos, dependendo do enredo e da abordagem de produção.
Diferenças Significativas Entre Gêneros:
- O gráfico destaca uma clara diferença nos orçamentos médios entre os gêneros mais comerciais e os de nicho. Gêneros como Animação e Aventura demandam grandes investimentos devido às suas produções complexas, enquanto gêneros como Documentário e Filmes Estrangeiros podem ser produzidos de forma mais econômica.
title_corpus = ' '.join(movies_metadata['title'])
overview_corpus = ' '.join(movies_metadata['overview'].astype('str'))
title_wordcloud = WordCloud(stopwords=STOPWORDS, background_color='white', height=2000, width=4000).generate(title_corpus)
overview_corpus_wordcloud = WordCloud(stopwords=STOPWORDS, background_color='white', height=2000, width=4000).generate(overview_corpus)
plt.figure(figsize=(16, 8))
plt.imshow(title_wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('Nuvem de Palavras - Títulos de Filmes', fontsize=20)
plt.show()
plt.figure(figsize=(16, 8))
plt.imshow(overview_corpus_wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('Nuvem de Palavras - Sinopse de Filmes', fontsize=20)
plt.show()
#Funções para analisar títulos e sinopses
# Função para pré-processar o texto
def preprocess_text(text):
# Converter para minúsculas
text = text.lower()
# Remover pontuação
text = re.sub(r'[^\w\s]', '', text)
# Tokenizar
tokens = word_tokenize(text)
# Remover stopwords
stop_words = set(stopwords.words('english'))
tokens = [word for word in tokens if word not in stop_words]
return tokens
# Função para obter frequências de palavras
def get_word_frequencies(text_series):
all_tokens = []
for text in text_series.dropna():
tokens = preprocess_text(text)
all_tokens.extend(tokens)
word_counts = Counter(all_tokens)
return word_counts
# Função para plotar nuvem de palavras
def plot_wordcloud(word_counts, title):
wordcloud = WordCloud(width=800, height=400, background_color='white').generate_from_frequencies(word_counts)
plt.figure(figsize=(15, 7.5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.title(title, fontsize=20)
plt.axis('off')
plt.show()
# Função para analisar palavras mais comuns por gênero
def plot_common_words_by_genre(movies_metadata, column, genre, top_n=10):
"""
Exibe as palavras mais comuns de títulos ou sinopses para um determinado gênero.
Parâmetros:
- movies_metadata: DataFrame contendo as informações dos filmes.
- column: Nome da coluna a ser analisada ('title' ou 'overview').
- genre: O gênero de interesse.
- top_n: O número de palavras mais frequentes para exibir.
"""
# Filtrar os filmes pelo gênero
genre_movies = movies_metadata[movies_metadata['genres'].apply(lambda x: genre in x)]
# Verificar se há dados suficientes
if genre_movies.empty:
print(f"Nenhum dado disponível para o gênero '{genre}' na coluna '{column}'.")
return
# Obter frequências das palavras
text_series = genre_movies[column].dropna()
word_counts = get_word_frequencies(text_series)
# Selecionar as top_n palavras mais comuns
common_words = word_counts.most_common(top_n)
if len(common_words) == 0:
print(f"Nenhuma palavra encontrada para o gênero '{genre}' na coluna '{column}'.")
return
# Separar palavras e frequências
words, counts = zip(*common_words)
# Criar o gráfico
plt.figure(figsize=(10, 6))
plt.bar(words, counts, color='skyblue')
plt.title(f'Palavras Mais Comuns no "{column}" para o Gênero "{genre}"')
plt.xlabel('Palavras')
plt.ylabel('Frequência')
plt.xticks(rotation=45)
plt.show()
# Função para analisar palavras mais comuns por década
def plot_common_words_by_decade(movies_metadata, column, decade, top_n=10):
"""
Exibe as palavras mais comuns de títulos ou sinopses para uma determinada década.
Parâmetros:
- movies_metadata: DataFrame contendo as informações dos filmes.
- column: Nome da coluna a ser analisada ('title' ou 'overview').
- decade: A década de interesse (ex: 1990 para a década de 1990-1999).
- top_n: O número de palavras mais frequentes para exibir.
"""
# Filtrar os filmes pela década
decade_movies = movies_metadata[movies_metadata['decade'] == decade]
# Verificar se há dados suficientes
if decade_movies.empty:
print(f"Nenhum dado disponível para a década '{decade}' na coluna '{column}'.")
return
# Obter frequências das palavras
text_series = decade_movies[column].dropna()
word_counts = get_word_frequencies(text_series)
# Selecionar as top_n palavras mais comuns
common_words = word_counts.most_common(top_n)
if len(common_words) == 0:
print(f"Nenhuma palavra encontrada para a década '{decade}' na coluna '{column}'.")
return
# Separar palavras e frequências
words, counts = zip(*common_words)
# Criar o gráfico
plt.figure(figsize=(10, 6))
plt.bar(words, counts, color='salmon')
plt.title(f'Palavras Mais Comuns no "{column}" para a Década de {decade}')
plt.xlabel('Palavras')
plt.ylabel('Frequência')
plt.xticks(rotation=45)
plt.show()
# Função para obter frequências de n-gramas
def get_ngrams_frequencies(text_series, n=2):
all_ngrams = []
for text in text_series.dropna():
tokens = preprocess_text(text)
n_grams = ngrams(tokens, n)
all_ngrams.extend(n_grams)
ngram_counts = Counter(all_ngrams)
return ngram_counts
# Função para plotar n-gramas
def plot_ngrams(ngram_counts, title, top_n=10):
common_ngrams = ngram_counts.most_common(top_n)
if len(common_ngrams) == 0:
print("Nenhum n-grama encontrado.")
return
ngrams_texts = [' '.join(ngram) for ngram, count in common_ngrams]
counts = [count for ngram, count in common_ngrams]
plt.figure(figsize=(10, 6))
plt.bar(ngrams_texts, counts, color='purple')
plt.title(title)
plt.xlabel('N-gramas')
plt.ylabel('Frequência')
plt.xticks(rotation=45)
plt.show()
# Função para obter pontuações de sentimento
def get_sentiment_scores(text_series):
sentiments = text_series.dropna().apply(lambda x: TextBlob(x).sentiment.polarity)
return sentiments
# Função para plotar sentimento por gênero
def plot_sentiment_by_genre(movies_metadata):
genres = set([genre for sublist in movies_metadata['genres'] for genre in sublist])
avg_sentiments = {}
for genre in genres:
genre_movies = movies_metadata[movies_metadata['genres'].apply(lambda x: genre in x)]
sentiments = get_sentiment_scores(genre_movies['overview'])
if len(sentiments) > 0:
avg_sentiments[genre] = sentiments.mean()
# Plotar
genres = list(avg_sentiments.keys())
sentiments = list(avg_sentiments.values())
plt.figure(figsize=(12,6))
plt.bar(genres, sentiments, color='orange')
plt.title('Sentimento Médio por Gênero')
plt.xlabel('Gênero')
plt.ylabel('Sentimento Médio')
plt.xticks(rotation=45)
plt.show()
# Função para plotar sentimento por década
def plot_sentiment_by_decade(movies_metadata):
decades = movies_metadata['decade'].dropna().unique()
avg_sentiments = {}
for decade in decades:
decade_movies = movies_metadata[movies_metadata['decade'] == decade]
sentiments = get_sentiment_scores(decade_movies['overview'])
if len(sentiments) > 0:
avg_sentiments[decade] = sentiments.mean()
# Ordenar por década
decades = sorted(avg_sentiments.keys())
sentiments = [avg_sentiments[decade] for decade in decades]
plt.figure(figsize=(12,6))
plt.plot(decades, sentiments, marker='o', linestyle='-')
plt.title('Sentimento Médio por Década')
plt.xlabel('Década')
plt.ylabel('Sentimento Médio')
plt.xticks(decades, rotation=45)
plt.grid(True)
plt.show()
# Análise de sentimento nas sinopses
sentiment_scores = get_sentiment_scores(movies_metadata['overview'])
plt.figure(figsize=(10,6))
plt.hist(sentiment_scores, bins=50, color='green')
plt.title('Distribuição dos Sentimentos nas Sinopses')
plt.xlabel('Polaridade')
plt.ylabel('Frequência')
plt.show()
# Análise de sentimento por gênero
plot_sentiment_by_genre(movies_metadata)
# Análise de sentimento por década
plot_sentiment_by_decade(movies_metadata)
Distribuição dos Sentimentos nas Sinopses¶
O gráfico acima exibe a distribuição dos sentimentos nas sinopses dos filmes, com polaridades variando entre -1 (sentimento extremamente negativo) e 1 (sentimento extremamente positivo).
Principais Insights:¶
Pico em Zero: A maior concentração de sinopses está em torno de 0.0, indicando que muitas das sinopses possuem um tom neutro. Esse resultado é esperado, uma vez que muitas descrições de filmes tendem a ser informativas e evitar emoções explícitas.
Tendência Levemente Positiva: Observa-se uma leve inclinação para o lado positivo da polaridade, com um número significativo de sinopses entre 0.0 e 0.3, o que sugere que, embora neutras, muitas sinopses carregam uma leve inclinação positiva.
Distribuição Simétrica: A distribuição se assemelha a uma curva simétrica em torno de zero, com uma pequena quantidade de sinopses fortemente negativas (à esquerda) e fortemente positivas (à direita), sugerindo que as emoções extremas são menos comuns nas sinopses.
Essa análise revela que a maioria das sinopses se mantém dentro de um intervalo neutro a levemente positivo, com poucas variações para os extremos emocionais. Isso pode ocorrer, talvez, por conta de na sinopse existir uma tendência de nâo revelar as principais informações do filme.
Análise do Gráfico de Sentimento Médio por Gênero¶
O gráfico de sentimento médio por gênero revela as diferenças nas emoções retratadas nas sinopses de filmes de diferentes gêneros.
Principais Insights:¶
Gêneros mais positivos: Gêneros como Animação e Família apresentam os sentimentos mais positivos, refletindo histórias leves e voltadas para o público jovem. Gêneros mais negativos: Horror e Mistério mostram os sentimentos mais negativos, o que é esperado devido às temáticas sombrias e emocionais que exploram.
Análise do Gráfico de Sentimento Médio por Década¶
O gráfico de sentimento médio por década mostra como as emoções nas sinopses dos filmes mudaram ao longo do tempo, e parecem ter uma tendência a títulos menos
movies_metadata.columns
Index(['belongs_to_collection', 'budget', 'genres', 'homepage', 'id',
'original_language', 'overview', 'popularity', 'production_companies',
'production_countries', 'release_date', 'revenue', 'runtime',
'spoken_languages', 'status', 'tagline', 'title', 'video',
'vote_average', 'vote_count', 'year', 'decade', 'ROI', 'profit',
'movieId', 'imdbId', 'tmdbId', 'countries', 'release_month', 'is_flop'],
dtype='object')
# Contagem de filmes que pertencem a uma franquia
franchise_count = movies_metadata['belongs_to_collection'].notnull().sum()
no_franchise_count = movies_metadata['belongs_to_collection'].isnull().sum()
print(f"Filmes que pertencem a uma franquia: {franchise_count}")
print(f"Filmes que não pertencem a uma franquia: {no_franchise_count}")
# Visualizando a distribuição
franchise_data = pd.DataFrame({
'Tipo de Filme': ['Franquia', 'Independente'],
'Quantidade': [franchise_count, no_franchise_count]
})
fig = px.bar(franchise_data, x='Tipo de Filme', y='Quantidade',
title='Quantidade de Filmes que Pertencem a uma Franquia vs Independentes',
color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])
fig.show()
Filmes que pertencem a uma franquia: 1030 Filmes que não pertencem a uma franquia: 2816
# Comparação de orçamento entre filmes de franquia e independentes
franchise_movies = movies_metadata[movies_metadata['belongs_to_collection'].notnull()]
independent_movies = movies_metadata[movies_metadata['belongs_to_collection'].isnull()]
franchise_avg_budget = franchise_movies['budget'].mean()
independent_avg_budget = independent_movies['budget'].mean()
print(f"Orçamento médio - Filmes de Franquia: {franchise_avg_budget}")
print(f"Orçamento médio - Filmes Independentes: {independent_avg_budget}")
# Visualizando a comparação
budget_data = pd.DataFrame({
'Tipo de Filme': ['Franquia', 'Independente'],
'Orçamento Médio (USD)': [franchise_avg_budget, independent_avg_budget]
})
fig = px.bar(budget_data, x='Tipo de Filme', y='Orçamento Médio (USD)',
title='Orçamento Médio: Filmes de Franquia vs Independentes',
color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])
fig.show()
Orçamento médio - Filmes de Franquia: 50004512.79805825 Orçamento médio - Filmes Independentes: 31842465.997514203
Orçamento Médio: Filmes de Franquia vs Independentes¶
O gráfico acima compara o orçamento médio entre filmes de franquia e filmes independentes, revelando uma diferença significativa nos valores investidos.
Principais Insights:¶
Filmes de Franquia:
- O orçamento médio dos filmes de franquia é de aproximadamente 50 milhões de dólares, um valor consideravelmente maior em comparação com os filmes independentes.
- A hipótese para isso pode ser que filmes que têm um bom retorno financeiro são mais propensos a se tornarem franquias. Assim, os estúdios investem mais em continuações, com base no sucesso inicial.
Filmes Independentes:
- Por outro lado, filmes independentes possuem um orçamento médio em torno de 31.8 milhões de dólares.
- Mesmo que alguns filmes independentes tenham um ótimo desempenho, eles geralmente começam com orçamentos menores, devido à incerteza sobre seu sucesso comercial.
Diferença de Orçamento:
- A diferença observada de cerca de 18 milhões de dólares entre franquias e filmes independentes pode estar relacionado a ser mais garantido o retorno em filmes que sâo franquias, pois os mesmos já são filmes consolidados, e por isso se tornaram franquias.
# Comparação de receita entre filmes de franquia e independentes
franchise_avg_revenue = franchise_movies['revenue'].mean()
independent_avg_revenue = independent_movies['revenue'].mean()
print(f"Receita média - Filmes de Franquia: {franchise_avg_revenue}")
print(f"Receita média - Filmes Independentes: {independent_avg_revenue}")
# Visualizando a comparação
revenue_data = pd.DataFrame({
'Tipo de Filme': ['Franquia', 'Independente'],
'Receita Média (USD)': [franchise_avg_revenue, independent_avg_revenue]
})
fig = px.bar(revenue_data, x='Tipo de Filme', y='Receita Média (USD)',
title='Receita Média: Filmes de Franquia vs Independentes',
color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])
fig.show()
Receita média - Filmes de Franquia: 212657195.83106795 Receita média - Filmes Independentes: 75103069.56676136
Receita Média: Filmes de Franquia vs Independentes¶
O gráfico compara a receita média entre filmes de franquia e filmes independentes, mostrando uma clara disparidade entre os dois.
Principais Insights:¶
Receita dos Filmes de Franquia:
- Os filmes de franquia apresentam uma receita média superior a 200 milhões de dólares, um valor consideravelmente maior do que o dos filmes independentes.
- Esse resultado apoia a hipótese de que filmes que obtêm grande retorno financeiro tendem a se tornar franquias. Ou seja, filmes que têm um bom desempenho nas bilheterias são frequentemente seguidos por sequências, o que resulta em franquias com receitas ainda maiores ao longo do tempo.
Receita dos Filmes Independentes:
- Os filmes independentes, por outro lado, têm uma receita média mais modesta, em torno de 80 milhões de dólares.
- Embora alguns filmes independentes possam atingir grande sucesso, a receita tende a ser menor, já que eles não possuem o mesmo apelo comercial, grandes campanhas de marketing ou a força de uma marca consolidada que as franquias muitas vezes têm.
Diferença de Receita:
- A diferença substancial na receita média entre filmes de franquia e filmes independentes pode não necessariamente significar que franquias geram mais retorno por si mesmas, mas sim que filmes de sucesso tendem a se transformar em franquias, ampliando seus ganhos ao longo do tempo com novas sequências.
# Comparando a média das notas (vote_average)
franchise_avg_rating = franchise_movies['vote_average'].mean()
independent_avg_rating = independent_movies['vote_average'].mean()
print(f"Média de Notas - Filmes de Franquia: {franchise_avg_rating}")
print(f"Média de Notas - Filmes Independentes: {independent_avg_rating}")
# Visualizando a comparação
rating_data = pd.DataFrame({
'Tipo de Filme': ['Franquia', 'Independente'],
'Média de Notas': [franchise_avg_rating, independent_avg_rating]
})
fig = px.bar(rating_data, x='Tipo de Filme', y='Média de Notas',
title='Média de Notas: Filmes de Franquia vs Independentes',
color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])
fig.show()
Média de Notas - Filmes de Franquia: 6.27621359223301 Média de Notas - Filmes Independentes: 6.446306818181818
Média de Notas: Filmes de Franquia vs Independentes¶
O gráfico compara a média de notas entre filmes de franquia e filmes independentes, revelando que ambos os tipos de filmes apresentam praticamente a mesma média.
Uma hipótese para essa semelhança é que os primeiros filmes de franquias costumam ser bem recebidos pelo público, mas isso não necessáriamente pe verdade para suas continuações. Seria necessário uma análise mais aprofundada para confirmar essa hipótese, com dados que infelizmente não estão contidos no dataset.
ratings = pd.read_csv('./the-movies-dataset/ratings_small.csv')
fig = px.histogram(ratings, x='rating', nbins=20, title='Distribuição das Notas dos Filmes')
fig.update_layout(xaxis_title='Nota', yaxis_title='Frequência')
fig.show()
Distribuição das Notas dos Filmes¶
O gráfico acima mostra a distribuição das notas atribuídas pelos usuários aos filmes, variando de 1 a 5. Um aspecto interessante observado na distribuição é a frequência de notas inteiras versus notas quebradas.
Principais Insights:¶
Notas Inteiras são mais Frequentes:
- Um ponto que se destaca é que notas inteiras (como 1, 2, 3, 4 e 5) são significativamente mais frequentes do que notas quebradas (como 1.5, 2.5, 3.5, etc.). Isso pode indicar que os usuários preferem usar valores inteiros ao avaliar filmes, possivelmente porque as notas inteiras são mais intuitivas e fáceis de escolher em uma escala de 1 a 5.
Ponto de Transição:
- A nota 4 também representa um ponto de transição interessante: à medida que subimos na escala de notas, as notas vão crescendo em frequência até atingir o pico na nota 4. Ou seja, a nota 4 é o ponto em que os filmes recebem avaliações mais positivas, sendo a última nota a crescer em frequência antes de uma leve queda na nota 5.
Menor Frequência de Notas Baixas:
- Notas abaixo de 3 aparecem com muito menos frequência, sugerindo que filmes com avaliações extremamente negativas são menos comuns. Isso pode refletir tanto a qualidade das produções quanto o fato de que os usuários tendem a não assistir ou avaliar filmes que eles acreditam que não irão gostar.
all_users = ratings['userId'].value_counts().reset_index()
all_users.columns = ['userId', 'count']
fig = px.bar(all_users, x='userId', y='count', title='Número de Avaliações por Usuário')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()
top_users = ratings['userId'].value_counts().reset_index().head(10)
top_users.columns = ['userId', 'count']
fig = px.bar(top_users, x='userId', y='count', title='Top 10 Usuários que Mais Avaliaram Filmes')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()
bottom_users = ratings['userId'].value_counts().reset_index().tail(10)
bottom_users.columns = ['userId', 'count']
fig = px.bar(bottom_users, x='userId', y='count', title='Bottom 10 Usuários que Mais Avaliaram Filmes')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()
Todos os usuários avaliaram pelo menos 20 filmes¶
# Filtrar notas acima de 3.5 para maior relevância
filtered_ratings = ratings[ratings['rating'] > 3.5]
# Criar matriz de transações com filmes avaliados
transactions = (filtered_ratings.groupby(['userId', 'movieId'])['rating']
.max().unstack().reset_index().fillna(0)
.set_index('userId'))
# Aplicar uma função binária, mantendo filmes com avaliação alta (>= 3.5)
transactions = transactions.applymap(lambda x: 1 if x > 3.5 else 0).astype(bool)
# Verificando as transações
print(f"Total de transações: {len(transactions)}")
print(f"Total de itens (filmes): {transactions.shape[1]}")
print(f"Quantidade de 1s na matriz de transações: {transactions.sum().sum()}")
# Ajustando min_support para gerar mais conjuntos frequentes
with tqdm(total=len(transactions.columns), desc="Calculando conjuntos frequentes") as pbar_frequent:
frequent_itemsets = apriori(transactions, min_support=0.1, use_colnames=True, low_memory=True) # Reduzido de 0.15 para 0.1
pbar_frequent.update(len(transactions.columns))
# Verificando o número de conjuntos frequentes encontrados
print(f"Total de conjuntos frequentes encontrados: {len(frequent_itemsets)}")
if frequent_itemsets.empty:
print("Nenhum conjunto frequente encontrado. Considere diminuir o min_support ou ajustar os dados.")
else:
# Calculando regras de associação com um min_lift mais baixo (ex.: 1.1) e min_confidence mais baixo (ex.: 0.5)
with tqdm(total=len(frequent_itemsets), desc="Calculando regras de associação") as pbar_rules:
rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1.1) # Reduzido de 1.2 para 1.1
pbar_rules.update(len(frequent_itemsets))
# Verificando se há regras de associação
if not rules.empty:
print(rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']])
else:
print("Nenhuma regra encontrada.")
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3131926791.py:10: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
Total de transações: 671 Total de itens (filmes): 6170 Quantidade de 1s na matriz de transações: 51568
Calculando conjuntos frequentes: 100%|██████████| 6170/6170 [00:01<00:00, 3707.81it/s]
Total de conjuntos frequentes encontrados: 533
Calculando regras de associação: 100%|██████████| 533/533 [00:00<00:00, 14934.23it/s]
antecedents consequents support confidence lift 0 (1) (260) 0.119225 0.503145 1.455216 1 (260) (1) 0.119225 0.344828 1.455216 2 (296) (1) 0.117735 0.313492 1.322976 3 (1) (296) 0.117735 0.496855 1.322976 4 (1) (318) 0.129657 0.547170 1.339967 ... ... ... ... ... ... 1555 (7153, 2571) (5952, 4993) 0.117735 0.868132 4.623147 1556 (5952) (4993, 2571, 7153) 0.117735 0.556338 4.497624 1557 (4993) (5952, 7153, 2571) 0.117735 0.496855 3.922235 1558 (2571) (5952, 4993, 7153) 0.117735 0.403061 2.436523 1559 (7153) (5952, 4993, 2571) 0.117735 0.598485 4.462037 [1560 rows x 5 columns]
Regras de Associação sobre Filmes Avaliados¶
A análise de regras de associação aplicada aos dados de filmes avaliados com nota igual ou superior a 3 gerou alguns insights importantes:
Filmes Combinados Frequentes:
- Os conjuntos frequentes são combinações de filmes que foram avaliados positivamente juntos por uma parte significativa dos usuários. Isso pode indicar tendências de preferência entre os usuários, sugerindo que determinados filmes são frequentemente vistos e bem avaliados em conjunto.
- Por exemplo, se um usuário avaliou positivamente o filme
A, é bastante provável que também tenha avaliado positivamente o filmeB.
Confiabilidade das Regras:
- A métrica de confidence nas regras geradas indica a probabilidade de um filme ser bem avaliado se outro filme já foi bem avaliado. Regras com alta confiança sugerem uma relação forte entre os filmes avaliados positivamente.
- Se a confiança para um par de filmes for, por exemplo, 0.60, isso indica que em 60% dos casos, quando o filme
Aé avaliado positivamente, o filmeBtambém o é.
Elevação (Lift):
- O lift é outra métrica importante que mede o quão maior é a probabilidade de os filmes serem avaliados juntos em relação à probabilidade de serem avaliados individualmente. Um lift acima de 1 indica uma correlação positiva, sugerindo que os filmes estão relacionados em termos de preferência do usuário.
- Um lift de 3, por exemplo, significa que os filmes são 3 vezes mais propensos a serem avaliados juntos do que individualmente.
Esses insights podem ser utilizados para:
- Recomendações personalizadas: Se um usuário já avaliou positivamente um filme de um conjunto frequente, é possível recomendar os outros filmes do mesmo conjunto.
- Identificar padrões de preferência: Os dados revelam padrões sobre os gêneros ou tipos de filmes que são mais frequentemente bem avaliados juntos.
# Função para substituir IDs por títulos de filmes
def substituir_ids_por_titulos(ids, movies_metadata):
filmes = [movies_metadata[movies_metadata['movieId'] == i]['title'].values[0] for i in ids]
return filmes
# Filtrar regras com alta confiança e lift
high_confidence_rules = rules[(rules['confidence'] > 0.7) & (rules['lift'] > 1.5)]
# Mostrar regras mais fortes ordenadas por lift
strongest_rules = high_confidence_rules.sort_values(by='lift', ascending=False).head(10)
# Substituir IDs dos filmes por títulos nas colunas 'antecedents' e 'consequents'
strongest_rules['antecedents'] = strongest_rules['antecedents'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))
strongest_rules['consequents'] = strongest_rules['consequents'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))
print("Regras de associação mais fortes (com títulos):")
print(strongest_rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']])
# Verificar filmes mais frequentemente avaliados juntos
most_frequent_sets = frequent_itemsets.sort_values(by='support', ascending=False).head(10)
# Substituir IDs por títulos no conjunto frequente
most_frequent_sets['itemsets'] = most_frequent_sets['itemsets'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))
print("\nConjuntos frequentes mais comuns (com títulos):")
print(most_frequent_sets)
# Resumo geral dos resultados
print(f"Total de transações: {len(transactions)}")
print(f"Total de conjuntos frequentes encontrados: {len(frequent_itemsets)}")
print(f"Total de regras de associação geradas: {len(rules)}")
Regras de associação mais fortes (com títulos):
antecedents \
1497 [The Lord of the Rings: The Fellowship of the ...
1496 [The Lord of the Rings: The Two Towers, Star W...
1480 [The Lord of the Rings: The Fellowship of the ...
1485 [The Lord of the Rings: The Return of the King...
1484 [The Lord of the Rings: The Return of the King...
1468 [The Lord of the Rings: The Two Towers, Star W...
1469 [The Lord of the Rings: The Fellowship of the ...
1467 [The Lord of the Rings: The Two Towers, The Em...
1470 [The Lord of the Rings: The Fellowship of the ...
1495 [The Lord of the Rings: The Two Towers, Return...
consequents support confidence \
1497 [The Lord of the Rings: The Two Towers, Star W... 0.101341 0.839506
1496 [The Lord of the Rings: The Fellowship of the ... 0.101341 0.772727
1480 [The Lord of the Rings: The Return of the King... 0.105812 0.747368
1485 [The Lord of the Rings: The Fellowship of the ... 0.105812 0.865854
1484 [The Lord of the Rings: The Fellowship of the ... 0.105812 0.922078
1468 [The Lord of the Rings: The Fellowship of the ... 0.110283 0.840909
1469 [The Lord of the Rings: The Two Towers, Star W... 0.110283 0.778947
1467 [The Lord of the Rings: The Fellowship of the ... 0.110283 0.902439
1470 [The Lord of the Rings: The Two Towers, The Em... 0.110283 0.718447
1495 [The Lord of the Rings: The Fellowship of the ... 0.101341 0.894737
lift
1497 6.401235
1496 6.401235
1480 6.115661
1485 6.115661
1484 6.006935
1468 5.939474
1469 5.939474
1467 5.878996
1470 5.878996
1495 5.828820
Conjuntos frequentes mais comuns (com títulos):
support itemsets
13 0.408346 [The Shawshank Redemption]
12 0.375559 [Pulp Fiction]
15 0.374069 [Forrest Gump]
28 0.356185 [The Silence of the Lambs]
10 0.345753 [Star Wars]
22 0.307004 [Schindler's List]
81 0.292101 [The Matrix]
49 0.281669 [The Empire Strikes Back]
30 0.274218 [Fargo]
84 0.268256 [American Beauty]
Total de transações: 671
Total de conjuntos frequentes encontrados: 533
Total de regras de associação geradas: 1560
def recomendar_filmes_por_preferencia(usuario, filmes_gostados, transacoes, regras_associacao, n_recomendacoes=3, min_confidence=0.7, min_support=0.3, min_lift=1.2):
"""
Função para recomendar filmes a um usuário com base em uma lista de filmes que ele gostou e regras de associação,
considerando confiança, suporte e lift mínimos.
Parâmetros:
- usuario: ID do usuário.
- filmes_gostados: Lista de IDs dos filmes que o usuário gostou.
- transacoes: DataFrame das transações de filmes.
- regras_associacao: DataFrame das regras de associação.
- n_recomendacoes: Número de recomendações desejadas.
- min_confidence: Confiança mínima das regras para considerar na recomendação.
- min_support: Suporte mínimo das regras para considerar na recomendação.
- min_lift: Lift mínimo das regras para considerar na recomendação.
Retorna:
- Lista de títulos de filmes recomendados.
"""
recomendacoes = []
# Iterar pelos filmes que o usuário gostou e buscar regras de associação
for filme in filmes_gostados:
# Filtrar as regras que têm o filme nos antecedentes e que atendem os critérios de confiança, suporte e lift mínimos
regras_filme = regras_associacao[
(regras_associacao['antecedents'].apply(lambda x: filme in x)) &
(regras_associacao['confidence'] >= min_confidence) &
(regras_associacao['support'] >= min_support) &
(regras_associacao['lift'] >= min_lift)
]
# Ordenar regras por confiança e lift
regras_filme = regras_filme.sort_values(by=['confidence', 'lift'], ascending=False)
# Iterar pelas regras e adicionar filmes recomendados
for _, regra in regras_filme.iterrows():
recomendados = regra['consequents']
for filme_recomendado in recomendados:
if filme_recomendado not in filmes_gostados and filme_recomendado not in recomendacoes:
recomendacoes.append(filme_recomendado)
if len(recomendacoes) >= n_recomendacoes:
break
if len(recomendacoes) >= n_recomendacoes:
break
# Verificar se há recomendações e se os filmes existem no metadata
if recomendacoes:
recomendacoes_nomes = movies_metadata[movies_metadata['movieId'].isin(recomendacoes)]['title'].tolist()
if not recomendacoes_nomes:
print(f"Não foi possível encontrar os títulos dos filmes recomendados para o usuário {usuario}. IDs dos filmes recomendados: {recomendacoes}")
return recomendacoes_nomes[:n_recomendacoes]
else:
print(f"Nenhuma recomendação foi gerada para o usuário {usuario} com base nos filmes que ele gostou.")
return []
# Função para selecionar um filme aleatório que o usuário gostou
def selecionar_filme_aleatorio_usuario(filtered_ratings, usuario_id):
filmes_usuario = filtered_ratings[filtered_ratings['userId'] == usuario_id]
if not filmes_usuario.empty:
filmes_avaliados = filmes_usuario[filmes_usuario['rating'] > 3]['movieId'].tolist()
if filmes_avaliados:
return random.choice(filmes_avaliados)
else:
print(f"Usuário {usuario_id} não avaliou filmes com nota superior a 3.")
return None
else:
print(f"Nenhum filme encontrado para o usuário {usuario_id}.")
return None
def recomendar_filmes(usuario, transacoes, regras_associacao, n_recomendacoes=3, min_confidence=0.7, min_support=0.3, min_lift=1.2):
"""
Função para recomendar filmes a um usuário com base em regras de associação, considerando confiança, suporte e lift mínimos.
Parâmetros:
- usuario: ID do usuário.
- transacoes: DataFrame das transações de filmes.
- regras_associacao: DataFrame das regras de associação.
- n_recomendacoes: Número de recomendações desejadas.
- min_confidence: Confiança mínima das regras para considerar na recomendação.
- min_support: Suporte mínimo das regras para considerar na recomendação.
- min_lift: Lift mínimo das regras para considerar na recomendação.
Retorna:
- Lista de títulos de filmes recomendados.
"""
# Obter a lista de filmes assistidos pelo usuário
filmes_usuario = transacoes.loc[usuario]
filmes_assistidos = filmes_usuario[filmes_usuario == 1].index.tolist()
recomendacoes = []
# Iterar pelos filmes assistidos e buscar regras de associação
for filme in filmes_assistidos:
# Filtrar as regras que têm o filme nos antecedentes e que atendem os critérios de confiança, suporte e lift mínimos
regras_filme = regras_associacao[
(regras_associacao['antecedents'].apply(lambda x: filme in x)) &
(regras_associacao['confidence'] >= min_confidence) &
(regras_associacao['support'] >= min_support) &
(regras_associacao['lift'] >= min_lift)
]
# Ordenar regras por confiança e lift
regras_filme = regras_filme.sort_values(by=['confidence', 'lift'], ascending=False)
# Iterar pelas regras e adicionar filmes recomendados
for _, regra in regras_filme.iterrows():
recomendados = regra['consequents']
for filme_recomendado in recomendados:
if filme_recomendado not in filmes_assistidos and filme_recomendado not in recomendacoes:
recomendacoes.append(filme_recomendado)
if len(recomendacoes) >= n_recomendacoes:
break
if len(recomendacoes) >= n_recomendacoes:
break
# Obter os títulos dos filmes recomendados
recomendacoes_nomes = movies_metadata[movies_metadata['movieId'].isin(recomendacoes)]['title'].tolist()
return recomendacoes_nomes[:n_recomendacoes]
# Selecionando 10 usuários aleatórios
random.seed(42) # Definindo uma semente para garantir que os resultados sejam reproduzíveis
random_users = filtered_ratings['userId'].drop_duplicates().sample(10).tolist()
for usuario_id in random_users:
recomendacoes = recomendar_filmes(usuario_id, transactions, rules, n_recomendacoes=3, min_confidence=0.7, min_support=0.1, min_lift=1.2)
print(f"Filmes recomendados para o usuário {usuario_id}: {recomendacoes}")
Filmes recomendados para o usuário 81: ['Pulp Fiction'] Filmes recomendados para o usuário 154: ['Star Wars', 'Pulp Fiction', 'The Empire Strikes Back'] Filmes recomendados para o usuário 197: ['Raiders of the Lost Ark', 'The Lord of the Rings: The Fellowship of the Ring', 'The Lord of the Rings: The Two Towers'] Filmes recomendados para o usuário 52: ['Pulp Fiction', 'The Silence of the Lambs'] Filmes recomendados para o usuário 174: [] Filmes recomendados para o usuário 266: ['Star Wars', 'Pulp Fiction', 'The Lord of the Rings: The Two Towers'] Filmes recomendados para o usuário 72: ['Star Wars', 'The Empire Strikes Back', 'The Lord of the Rings: The Two Towers'] Filmes recomendados para o usuário 332: ['Pulp Fiction'] Filmes recomendados para o usuário 219: ['The Silence of the Lambs', 'The Empire Strikes Back', 'The Lord of the Rings: The Two Towers'] Filmes recomendados para o usuário 350: ['Pulp Fiction', 'The Lord of the Rings: The Two Towers', 'The Lord of the Rings: The Return of the King']
Explicação do Código de Recomendação de Filmes com Regras de Associação¶
Este código utiliza regras de associação para recomendar filmes para os usuários com base em suas avaliações anteriores. Vamos detalhar as principais etapas do processo e o objetivo final.
1. Filtragem de Avaliações:¶
Primeiro, filtramos as avaliações dos filmes, mantendo apenas aquelas com notas maiores ou iguais a 3. Isso significa que consideramos apenas os filmes que os usuários avaliaram de forma positiva, ou seja, que provavelmente gostaram.
2. Criação da Matriz de Transações:¶
Depois da filtragem, uma matriz de transações é criada, onde:
- As linhas representam os usuários.
- As colunas representam os filmes.
- Se o valor da célula for 1, significa que o usuário avaliou aquele filme positivamente (nota maior ou igual a 3). Caso contrário, o valor será 0. A matriz resultante serve como base para o cálculo das associações entre os filmes.
3. Apriori e Conjuntos Frequentes:¶
Em seguida, utilizamos o algoritmo Apriori para encontrar conjuntos frequentes de filmes. Um conjunto frequente é um grupo de filmes que frequentemente aparecem juntos nas avaliações positivas dos usuários. Por exemplo, se muitos usuários que assistiram a "Filme A" também assistiram a "Filme B", esses dois filmes formam um conjunto frequente.
A métrica min_support foi definida como 0.1, ou seja, consideramos conjuntos frequentes aqueles que aparecem em pelo menos 10% das transações.
4. Regras de Associação:¶
Com os conjuntos frequentes encontrados, o próximo passo é gerar regras de associação usando o algoritmo. As regras de associação ajudam a identificar relações do tipo:
- "Se o usuário assistiu a Filme A, ele também pode gostar de Filme B."
Essas regras são calculadas com base em métricas como:
- Suporte: Proporção de transações onde os filmes apareceram juntos.
- Confiança: A probabilidade de o usuário assistir a Filme B dado que já assistiu a Filme A.
- Lift: A medida de quão mais provável é que o usuário veja Filme B depois de assistir Filme A, comparado ao acaso.
5. Recomendação de Filmes:¶
A função recomendar_filmes é responsável por gerar recomendações personalizadas para cada usuário. Para isso:
- Primeiro, verificamos os filmes que o usuário já assistiu.
- Depois, com base nas regras de associação geradas, recomendamos filmes que ele ainda não assistiu, mas que têm uma alta probabilidade de serem do interesse dele, com base nos filmes que ele já gostou.
usuarios_com_avaliacoes_altas = filtered_ratings['userId'].drop_duplicates()
usuarios_selecionados = usuarios_com_avaliacoes_altas.sample(2, random_state=42).tolist()
# Recomendando filmes para os 2 usuários selecionados
for usuario_id in usuarios_selecionados:
# Selecionar aleatoriamente um filme que o usuário gostou
filme_aleatorio = selecionar_filme_aleatorio_usuario(filtered_ratings, usuario_id)
if filme_aleatorio is not None:
# Obter o título do filme selecionado
filme_selecionado_titulo = movies_metadata[movies_metadata['movieId'] == filme_aleatorio]['title'].values
if filme_selecionado_titulo.size > 0:
filme_selecionado_titulo = filme_selecionado_titulo[0]
# Fazer a recomendação para o usuário com base no filme selecionado
recomendacoes = recomendar_filmes_por_preferencia(usuario_id, [filme_aleatorio], transactions, rules, n_recomendacoes=1, min_confidence=0.5, min_support=0.1, min_lift=1.2)
# Printar o ID do usuário e o filme selecionado
print(f"Usuário {usuario_id}: Gostou do filme '{filme_selecionado_titulo}' (ID {filme_aleatorio})")
print(f"Filmes recomendados para o usuário {usuario_id}: {recomendacoes}\n")
else:
print(f"Título não encontrado para o filme com ID {filme_aleatorio} do usuário {usuario_id}.")
else:
print(f"Não foi possível selecionar um filme para o usuário {usuario_id}.")
Usuário 362: Gostou do filme 'One Flew Over the Cuckoo's Nest' (ID 1193) Filmes recomendados para o usuário 362: ['The Godfather'] Usuário 159: Gostou do filme 'Blade Runner' (ID 541) Filmes recomendados para o usuário 159: ['Star Wars']